Question

I have an object in a worker thread, which I can instruct to stop running. I can implement this using a bool or an AutoResetEvent:

boolean:

private volatile bool _isRunning;

public void Run() {
    while (_isRunning)
    {
        doWork();
        Thread.Sleep(1000);
    }
}

AutoResetEvent:

private AutoResetEvent _stop;

public void Run() {
    do {
        doWork();
    } while (!_stop.WaitOne(1000));
}

The Stop() method would then set _isRunning to false, or call _stop.Set().

Apart from that the solution with AutoResetEvent may stop a little faster, is there any difference between these method? Is one "better" than the other?

Était-ce utile?

La solution

C# volatile does not provide all the guaranties. It may still read stale data. Better to use underlying OS synchronisation mechanism as it provides much stronger guaranties.

All this in great depth discussed by Eric Lippert (really worth reading), here is short quote:

In C#, "volatile" means not only "make sure that the compiler and the jitter do not perform any code reordering or register caching optimizations on this variable". It also means "tell the processors to do whatever it is they need to do to ensure that I am reading the latest value, even if that means halting other processors and making them synchronize main memory with their caches".

Actually, that last bit is a lie. The true semantics of volatile reads and writes are considerably more complex than I've outlined here; in fact they do not actually guarantee that every processor stops what it is doing and updates caches to/from main memory. Rather, they provide weaker guarantees about how memory accesses before and after reads and writes may be observed to be ordered with respect to each other. Certain operations such as creating a new thread, entering a lock, or using one of the Interlocked family of methods introduce stronger guarantees about observation of ordering. If you want more details, read sections 3.10 and 10.5.3 of the C# 4.0 specification.

Frankly, I discourage you from ever making a volatile field. Volatile fields are a sign that you are doing something downright crazy: you're attempting to read and write the same value on two different threads without putting a lock in place. Locks guarantee that memory read or modified inside the lock is observed to be consistent, locks guarantee that only one thread accesses a given hunk of memory at a time, and so on.

Autres conseils

Volatile isn't good enough, but practically it will always work because the operating system scheduler will always take a lock eventually. And will work well on a core with a strong memory model, like x86 which burns a lot of juice to keep caches synchronized between cores.

So what really only matters is how quickly a thread will respond to the stop request. It is easy to measure, just start a Stopwatch in the control thread and record the time after the while loop in the worker thread. The results I measured from repeating taking 1000 samples and taking the average, repeated 10 times:

volatile bool, x86:         550 nanoseconds
volatile bool, x64:         550 nanoseconds
ManualResetEvent, x86:     2270 nanoseconds
ManualResetEvent, x64:     2250 nanoseconds
AutoResetEvent, x86:       2500 nanoseconds
AutoResetEvent, x64:       2100 nanoseconds
ManualResetEventSlim, x86:  650 nanoseconds
ManualResetEventSlim, x64:  630 nanoseconds

Beware that the results for volatile bool are very unlikely to look that well on a processor with a weak memory model, like ARM or Itanium. I don't have one to test.

Clearly it looks like you want to favor ManualResetEventSlim, giving good perf and a guarantee.

One note with these results, they were measured with the worker thread running a hot loop, constantly testing the stop condition and not doing any other work. That's not exactly a good match with real code, a thread won't typically check the stop condition that often. Which makes the differences between these techniques largely inconsequential.

IMHO the AutoResetEvent is better, because you can't forget the crucial volatile keyword in this case.

Before using the volatile keyword, you should read this and, I guess, when researching multi-threading you could read the whole http://www.albahari.com/threading/ article.

It explains the subtleties of the volatile keyword and why its behaviour can be unexpected.


You'll note, that when using volatile, reads and writes can get reordered which could result in an extra iteration in closely concurrent situations. In this case you may have to wait for around one extra second.


After looking, I don't think your code works for several reasons,

The "boolean:" snippet always sleeps for approximately a second, probably not what you want.

The "AutoResetEvent:" snippet doesen't instantiate _stop and, will always run doWork() at least once.

It depends if the above snippet is all you are doing or not. The AutoReset event, as its name suggests, gets reset after the WaitOne is passed. This means that you could then use it again straightaway, rather than with the bool, having to set it back to true.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top