2016-08-19 2 views
0

Это упрощенный код, просто иллюстрирующий проблему, которую я пытаюсь решить (не скомпилировал, поэтому, пожалуйста, игнорируйте любые синтаксические ошибки). Предположим, у меня есть ProducerProxy как:Правильно избавляйтесь от производителя событий от другой нити

public class ProducerProxy : IDisposable { 
    public event EventHandler<EventArgs> NotificationEvent; 
    private volatile bool itsKeepProducing = true; 

    public DoStuff() { 
     Task.Factory.StartNew(() => { 
     while (itsKeepProducing) { 
      RaiseNotificationEvent(); 
      Thread.Sleep(100); 
     } 
     } 
    } 

    public void Dispose() { 
     itsKeepProducing = false; 
     DestroySomeStuff(); 
    } 
} 

Пусть теперь у меня есть класс, который использует этот ProducerProxy:

public class Consumer : IDisposable { 
    private ProducerProxy itsProducerProxy; 

    public void Consumer() { 
     itsProducerProxy = new ProducerProxy(); 
     itsProducerProxy.NotificationEvent += OnNotificationEvent; 
    } 

    public void Start() { 
     itsProducerProxy.DoStuff(); 
    } 

    public void OnNotificationEvent(object sender, EventArgs args) { 
     DealWithNotification(args); //this could take some time maybe 1-2 seconds 
    } 

    public void Dispose() { 
     //how do I dispose of the producer here? 
     //I can't just do the following because notifications might still be processing in OnNotification event: 
     if (itsProducerProxy != null) { 
      itsProducerProxy.NotificationEvent -= OnNotificationEvent; 
      itsProducerProxy.Dispose(); 
      itsProducerProxy = null; 
     } 
    } 

Так что мой вариант использования (да, это должно быть сделано с помощью try/catch или с помощью using но что отвлекает от вопроса - просто иллюстрирующей точку)

var consumer = new Consumer(); 
consumer.Start(); 
... //do some stuff 
consumer.Dispose(); 

что такое правильная/надлежащей поточно-реализация для Consumer.Dispose()? или, возможно, для Producer.Dispose()?

+0

'// это может занять некоторое время, может быть, 1-2 секунды, может ли это понять, что во время процесса может произойти утечка? Может ли он проверить CancellationToken, выполняя свою работу, понимая, когда IsCancellationRequested истинна, объект удален и пришло время прекратить работу? – Will

+0

@ Будет, возможно, удаление может происходить во время обработки события. Не стесняйтесь публиковать решение, используя CancellationToken. – Denis

+0

Наверное, самым безопасным будет просто убедиться, что ваш «EventArgs» несет в нем все необходимое состояние. Тогда не имеет значения, происходит ли удаление промежуточной обработки. На самом деле это зависит от того, насколько вы полагаетесь на «ProducerProxy» внутри вашего обработчика. –

ответ

1

Вы можете использовать кооперативную шаблон отмены нить, передавая CancellationToken в процесс ...

public class Consumer : IDisposable {  
    private ProducerProxy itsProducerProxy; 

    // how we signal others that we are disposed 
    private CancellationTokenSource _cts = new CancellationTokenSource(); 

    /* SNIP */ 

    public void OnNotificationEvent(object sender, EventArgs args) { 
     // We now provide the inner process with the cancellation token 
     DealWithNotification(_cts.Token); 
    } 

    public void Dispose() 
    { 
     // not thread safe but you get the gist 
     if (_cts!= null) { 
      _cts.Cancel(); 
      _cts.Dispose(); 
      _cts = null; 
     } 
     /* SNIP */ 
    } 
} 

где внутренний процесс короткого замыкания, когда отмена была запрошена

private void DealWithNotification(CancellationToken token) 
{ 
    if(token.IsCancellationRequested) return; 
    var foo = "omgwtflol" + bar; 
    if(token.IsCancellationRequested) return; 
    Thread.Sleep(2); 
    if(token.IsCancellationRequested) return; 
    var reallyEveryTime = File.ReadAllBytes(foo); 
    if(token.IsCancellationRequested) return; 
    foreach(var b in reallyEveryTime) 
    { 
     if(token.IsCancellationRequested) return; 
     InnerProcess(token); 
    } 
    // etc etc etc you get the idea 
} 
+0

Я не думаю, что это было бы потокобезопасно, потому что, как только вы проверите отмену звонка в DealWithNotification, он может быть установлен сразу после вашей проверки, но до выполнения следующего оператора. Может быть, токен нужно проверить в стиле блокировки в 'OnNotificationEvent' перед выполнением' DealWithNotification'? – Denis

+0

@Denis Это называется * «аннулирование совместной работы» * по какой-либо причине. Вы должны сотрудничать. Это ** является потокобезопасным, в смысле, что 'IsCancellationRequested' не будет бросать, если вы обращаетесь к нему из нескольких потоков, и нет никакого состояния гонки между потоками, читающими его, и потоками, вызывающими' Cancel() 'в источнике токена. Конечно, нет гарантии, что один тик, когда вы проверите его, значение будет ложным, а следующий тик теперь прав. Если вы должны отменить текущую инструкцию по утилизации, этот шаблон вам не подходит. Подумайте о изоляции потоков и используйте Abort() для отмены. – Will

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