简体   繁体   中英

guarantee that up-to-date value of variable is always visible to several threads on multi-processor system

I'm using such configuration:

  • .NET framework 4.5
  • Windows Server 2008 R2
  • HP DL360p Gen8 (2 * Xeon E5-2640, x64)

I have such field somewhere in my program:

protected int HedgeVolume;

I access this field from several threads. I assume that as I have multi-processor system it's possible that this threads are executing on different processors.

What should I do to guarantee that any time I use this field the most recent value is "read"? And to make sure that when I "write" value it become available to all other threads immediately?

What should I do?

  • just leave field as is.
  • declare it volatile
  • use Interlocked class to access the field
  • use .NET 4.5 Volatile.Read , Volatile.Write methods to access the field
  • use lock

I only need simplest way to make my program work on this configuration I don't need my program to work on another computers or servers or operation systems. Also I want minimal latency so I'm looking for fastest solution that will always work on this standard configuration (multiprocessor intel x64, .net 4.5).

Your question is missing one key element... How important is the integrity of the data in that field?

volatile gives you performance, but if a thread is currently writing changes to the field, you won't get that data until it's done, so you might access out of date information, and potentially overwrite changes another thread is currently doing. If the data is sensitive, you might get bugs that would get very hard to track. However, if you are doing very quick update, overwrite the value without reading it and don't care that once in a while you get outdated (by a few ms) data, go for it.

lock guaranty that only one thread can access the field at a time. You can put it only on the methods that write the field and leave the reading method alone. The down side is, it is slow, and may block a thread while another is performing its task. However, you are sure your data stay valid.

Interlock exist to shield yourself from the scheduler context switch. My opinion? Don't use it unless you know exactly why you would be using it and exactly how to use it. It gives options, but with great options comes great problematic. It prevents a context switch while a variable is being update. It might not do what you think it does and won't prevent parallel threads from performing their tasks simultaneously.

You want to use Volatile.Read() .

As you are running on x86, all writes in C# are the equivalent of Volatile.Write() , you only need to use this for Itanium.

Volatile.Read() will ensure that you get the latest copy regardless of which thread last wrote it.

There is a fantastic write up here, C# Memory Model Explained

Summary of it includes,

On some processors, not only must the compiler avoid certain optimizations on volatile reads and writes, it also has to use special instructions. On a multi-core machine, different cores have different caches. The processors may not bother to keep those caches coherent by default, and special instructions may be needed to flush and refresh the caches.

Hopefully that much is obvious, other than the need for volatile to stop the compiler from optimising it, there is the processor as well.

However, in C# all writes are volatile (unlike say in Java), regardless of whether you write to a volatile or a non-volatile field. So, the above situation actually never happens in C#. A volatile write updates the thread's cache, and then flushes the entire cache to main memory.

You do not need Volatile.Write() . More authoratitive source here, Joe Duffy CLR Memory Model . However, you may need it to stop the compiler reordering it.

Since all C# writes are volatile, you can think of all writes as going straight to main memory. A regular, non-volatile read can read the value from the thread's cache, rather than from main

You need Volatile.Read()

When you start designing a concurrent program, you should consider these options in order of preference:

1) Isolation: each thread has it's own private data
2) Immutability: threads can see shared state, but it never changes
3) Mutable shared state: protect all access to shared state with locks

If you get to (3), then how fast do you actually need this to be?

Acquiring an uncontested lock takes in the order of 10ns ( 10 -8 seconds ) - that's fast enough for most applications and is the easiest way to guarantee correctness.

Using any of the other options you mention takes you into the realm of low-lock programming, which is insanely difficult to get correct.


If you want to learn how to write concurrent software, you should read these:

Intro: Joe Albahari's free e-book - will take about a day to read

Bible: Joe Duffy's "Concurrent Programming on Windows" - will take about a month to read

Depends what you DO. For reading only, volatile is easiest, interlocked allows a little more control. Lock is unnecessary as it is more ganular than the problem you describe. Not sure about Volatile.Read/Write, never used them.

volatile - bad, there are some issues (see Joe Duffy's blog)

if all you do is read the value or unconditionally write a value - use Volatile.Read and Volatile.Write

if you need to read and subsequently write an updated value - use the lock syntax. You can however achieve the same effect without lock using the Interlocked classes functionality, but this is more complex (involves CompareExchange s to ensure that you are updating the read value ie has not been modified since the read operation + logic to retry if the value was modified since the read).

From this i can understand that you want to be able to read the last value that it was writtent in a field. Lets make an analogy with the sql concurency problem of the data. If you want to be able to read the last value of a field you must make atomic instructions. If someone is writing a field all of the threads must be locked for reading until that thread finished the writing transaction. After that every read on that thread will be safe. The problem is not with reading as it is with writing. A lock on that field whenever its writtent should be enough if you ask me ...

First have a look here: Volatile vs. Interlocked vs. lock

The volatile modifier shurely is a good option for a multikernel cpu.

But is this enough? It depends on how you calculate the new HedgeVolume value!

  • If your new HedgeVolume does not depend on current HedgeVolume then your done with volatile.

  • But if HedgeVolume[x] = f(HedgeVolume[x-1]) then you need some thread synchronisation to guarantee that HedgeVolume doesn't change while you calculate and assign the new value. Both, lock and Interlocked szenarios would be suitable in this case.

I had a similar question and found this article to be extremely helpful. It's a very long read, but I learned a LOT!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM