2011-10-11 62 views
2

我需要取消使用ThreadPool.QueueUserWorkItem(...)开始的后台任务。我知道一个BackgroundWorker特别为这类事情构建,但我相信在这种情况下它是过度的,因为不涉及用户界面。通过取消,我只是指强制完成回调方法。取消ThreadPool.QueueUserWorkItem任务

在我的课堂上添加类似以下内容的缺陷是什么?

// Cancellation Property. 
private bool _canceled; 
public bool CancelTask 
{ 
    get { return _canceled; } 
    set { _canceled = value; } 
} 

public void DoSomeTask() 
{ 
    int iterations = 50; 
    ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolCallback), iterations); 
} 

private void ThreadPoolCallback(object state) 
{ 
    if (_canceled) 
     return; // don't even start. 

    int iterations = (int)state; 
    for (int i = 0; !_canceled && i < iterations; i++) 
    { 
     // 
     // do work ... 
     // 

     // This allows you to cancel in the middle of an iteration... 
     if (_canceled) 
      break; 
    } 
} 

有没有更好的方法?

+0

我已经多次使用类似的技术,没有任何问题;我看到的唯一问题是如果多个线程可能会尝试写入'_canceled'。但对于一个简单的“一次写入,一次读取,退出”一种标志,我从来没有遇到过问题。虽然我很好奇听到其他想法。 – CodingGorilla

+1

只要确保_canceled被正确同步。我建议使用互锁函数来修改该变量,以便存在内存障碍。 –

+2

您必须使用* volatile *作为_canceled成员声明*或*使用CancelTask​​属性获取器。如果没有这个,你的工作者方法永远不会看到更新的确有可能。 BGW的开销很少,而且代码已经被数千人测试并由其他人维护。 –

回答

3

我会使用CancelTask​​()方法而不是属性。关键是呼叫者应该能够取消任务,但是没有人应该能够取消任务。

然后,您需要确保_cancelled的读取和写入具有适当的内存屏障,否则一个线程可能无法观察到另一个线程所做的更改。为此,我会使用Thread.VolatileWrite(在CancelTask​​中)和Thread.VolatileRead(在你的循环中)