2009-12-18 3 views
3

Я не понимаю, почему в этой реализации stopped не volatile - Если другой поток обновит это, он будет правильно отображен?Отключение рабочих потоков изящно

Во-вторых, тестирует (!Stopping) атомный?

using System; 
using System.Threading; 

/// <summary> 
/// Skeleton for a worker thread. Another thread would typically set up 
/// an instance with some work to do, and invoke the Run method (eg with 
/// new Thread(new ThreadStart(job.Run)).Start()) 
/// </summary> 
public class Worker 
{ 
    /// <summary> 
    /// Lock covering stopping and stopped 
    /// </summary> 
    readonly object stopLock = new object(); 
    /// <summary> 
    /// Whether or not the worker thread has been asked to stop 
    /// </summary> 
    bool stopping = false; 
    /// <summary> 
    /// Whether or not the worker thread has stopped 
    /// </summary> 
    bool stopped = false; 

    /// <summary> 
    /// Returns whether the worker thread has been asked to stop. 
    /// This continues to return true even after the thread has stopped. 
    /// </summary> 
    public bool Stopping 
    { 
     get 
     { 
      lock (stopLock) 
      { 
       return stopping; 
      } 
     } 
    } 

    /// <summary> 
    /// Returns whether the worker thread has stopped. 
    /// </summary> 
    public bool Stopped 
    { 
     get 
     { 
      lock (stopLock) 
      { 
       return stopped; 
      } 
     } 
    } 

    /// <summary> 
    /// Tells the worker thread to stop, typically after completing its 
    /// current work item. (The thread is *not* guaranteed to have stopped 
    /// by the time this method returns.) 
    /// </summary> 
    public void Stop() 
    { 
     lock (stopLock) 
     { 
      stopping = true; 
     } 
    } 

    /// <summary> 
    /// Called by the worker thread to indicate when it has stopped. 
    /// </summary> 
    void SetStopped() 
    { 
     lock (stopLock) 
     { 
      stopped = true; 
     } 
    } 

    /// <summary> 
    /// Main work loop of the class. 
    /// </summary> 
    public void Run() 
    { 
     try 
     { 
      while (!Stopping) 
      { 
       // Insert work here. Make sure it doesn't tight loop! 
       // (If work is arriving periodically, use a queue and Monitor.Wait, 
       // changing the Stop method to pulse the monitor as well as setting 
       // stopping.) 

       // Note that you may also wish to break out *within* the loop 
       // if work items can take a very long time but have points at which 
       // it makes sense to check whether or not you've been asked to stop. 
       // Do this with just: 
       // if (Stopping) 
       // { 
       //  return; 
       // } 
       // The finally block will make sure that the stopped flag is set. 
      } 
     } 
     finally 
     { 
      SetStopped(); 
     } 
    } 
} 

редактировать

Этот код из an article written by Jon Skeet.

+2

Я не понимаю, как это связано с Джоном Скитом? – YOU

+0

@ S.Mark: Его 'из страницы Jon Skeets об отключении рабочей нити. –

+2

@ S.Mark ... все, что связано с программированием, связано с Jon Skeet .... Это должен быть факт Jon Skeet! Http: //meta.stackexchange.com/questions/9134/jon-skeet-facts – James

ответ

6

Потому что он доступен только в пределах lock. A lockтакже действует, чтобы убедиться, что вы видите обновленные значения.

Re atomicity (и я предполагаю, что вы действительно подразумеваете синхронизацию здесь?); он не имеет никаких шансов; даже если Stopping синхронизирован, мы не можем больше доверять тому, что значение обновлено, как только мы выходим из lock, который у нас есть. Следовательно, !Stopping не более или менее синхронизирован, чем Stopping. Важно то, что мы знаем, что мы, по крайней мере, недавно проверили. Существует край, где флаг изменен сразу после мы проверяем, но это нормально: когда мы проверяли, было правдой, что мы должны продолжить.

+0

Я понял это? Каждый раз, когда происходит блокировка, регистры очищаются, и вы никогда не читаете устаревшие значения? Положите иначе, если я получу блокировку в узком цикле, тогда мне не нужна изменчивость? Когда-либо? –

+0

Пока все ** другие ** потоки только обновляют поле, когда они удерживают (тот же) замок, тогда да. Если другие потоки обновляют поле * без * получения блокировки, все ставки отключены. –

1

См. this entry on SO. Это объясняет, почему блокировка предпочтительнее летучих.

+0

Но разве этот пример не попадает в категорию «Какая волатильность на самом деле хороша для» в связанной записи?Нет способа установить останов или остановку на false, поэтому не может быть никаких условий гонки. – Niki

2

Поведение этого кода определяется в разделе 3.10 Спецификация языка C#:

Исполнение C# программных средств, таких, что побочные эффекты каждого выполняющегося потока сохраняются в критических точках выполнения. Побочный эффект определяется как чтение или запись изменчивого поля, запись в энергонезависимую переменную, запись на внешний ресурс и выброс исключения. Критические точки выполнения, в которых должен сохраняться порядок этих побочных эффектов, являются ссылками на изменчивые поля (§10.5.3), операторы блокировки (§8.12), а также создание и прекращение потоков.

Другими словами, оператор блокировки является достаточной гарантией, чтобы избежать необходимости объявлять остановленное поле изменчивым.

Как это правило реализуется компилятором JIT - интересный вопрос, учитывая, что оператор блокировки просто вызывает методы Monitor.Enter() и Exit(). Я не думаю, что у него есть специальные знания этих методов, я думаю, что это побочный эффект ввода блока try, который начинается после вызова Enter(). Но это всего лишь предположение.

Смежные вопросы