检测 ThreadPool 工作项是否已完成/等待完成

4
无论出于何种原因,ThreadPoolQueueWorkItem方法不返回IAsyncResult或其他处理工作项的句柄,这使得无法等待工作项完成。虽然有RegisterWait...方法,但需要传递一个WaitHandle,而创建它们是昂贵的(请参阅IAsyncResult文档,建议在请求时再创建WaitHandle)。任务并行库将解决这个问题,但要等很长时间才能使用。那么,这种设计是否存在任何问题:
public class Concurrent<T> {
    private ManualResetEvent _resetEvent;
    private T _result;

    public Concurrent(Func<T> f) {
        ThreadPool.QueueUserWorkItem(_ => {
                                         _result = f();
                                         if (_resetEvent != null)
                                             _resetEvent.Set();
                                     });
    }

    public WaitHandle WaitHandle {
        get {
            if (_resetEvent == null)
                _resetEvent = new ManualResetEvent(_result != null);
            return _resetEvent;
        }

    ...

编辑: 我提出了一个关于使用异步委托而不是线程池时可能出现的问题的后续问题。

查看问题

2个回答

6

好的,您在获取WaitHandle和设置它之间存在竞态条件。如果调用者稍微晚一点,您真的希望他们永远等待吗?

您应该进行适当的锁定,并保持“我已完成”的标志,以便如果在完成后创建WaitHandle,则在返回它之前设置它。

我个人还会编写一个静态工厂方法,而不仅仅是使用公共构造函数 - 或者将其制作为“创建并然后显式启动”模式。在构造函数中排队工作项感觉很奇怪。


注意,我将_result != null作为ManualResetEvent构造函数的参数传递。(对于结构体不起作用,但是思想是相同的。)_result != null在这里充当完成标志(当然不是在实际实现中)。是的,这里需要谨慎地加锁。(续...) - Alexey Romanov
一个静态工厂方法是不言而喻的,我很少编写没有相应静态方法的通用公共构造函数。 - Alexey Romanov
我不会一开始就将构造函数设为公共的。你还应该考虑返回 null 的方法... - Jon Skeet
就像我之前所说的那样,在实际的实现过程中不会使用 _result != null(如果方法抛出异常,这也会导致工作失误)。 - Alexey Romanov

3

  1. Concurrent 提供了一个更易于使用的 API(我认为);
  2. Concurrent 只是 Future<T> 的一个子类型,还有其他工作方式不同的类型(如惰性值、承诺);
  3. (最重要的)因为我没有注意到 Concurrent 可以实现为异步委托的外观。
- Alexey Romanov
啊,我一开始就觉得这与期货有关。 :-) - user49572

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接