异步多线程异常处理?

6

我希望在我的异步编程(beginInvoke/endInvoke)中有一种异常处理方法,如果任何一个线程(beginInvoke)失败,那么我希望所有其他异步处理线程停止工作。请提供一些解决方案?以下是我附加的示例代码:

public List<ThreadResultDto> SendMailAsynch(List<ThreadRequestDto>  requestDto)
{
    List<ThreadResultDto> resultDto = new List<ThreadResultDto>();
    List<IAsyncResult> asyncResults = new List<IAsyncResult>();

    foreach (ThreadRequestDto t in requestDto)
    {
        //Create a delegate.
        DoSomeAsynchWorkDelegate del = new DoSomeAsynchWorkDelegate(DoSomeAsynchWork);
        // Initiate the asynchronous call
        IAsyncResult a = del.BeginInvoke(t,null, del);
        //IAsyncResult a = del.BeginInvoke(t, null,null);
        asyncResults.Add(a);
    }

    foreach (IAsyncResult ar in asyncResults)
    {
        // wait for each one to complete, then call EndInvoke, passing in the IAsyncResult.
        // We cast ar.AsyncState to a DoSomeAsynchWorkDelegate, as we passed it in as the second parameter to BeginInvoke. 
        ar.AsyncWaitHandle.WaitOne();

        //AsyncState property of IAsyncResult is used to get the delegate that was used to call that method
        DoSomeAsynchWorkDelegate del = (DoSomeAsynchWorkDelegate)ar.AsyncState;

        // Call EndInvoke to get the result.  Add the result to the list of items. 
        resultDto.Add(del.EndInvoke(ar));
    }

    return resultDto;
}

1
顺便提一下,异步的通用缩写是 Async,而不是 Asynch :) - Richard Szalay
1个回答

2
最好的方法可能是使用共享的 ManualResetEvent
例如:
class MyClass
{
    private ManualResetEvent workFailedEvent = new ManualResetEvent(false);

    public List<ThreadResultDto> SendMailAsynch(List<ThreadRequestDto>  requestDto)
    {
        workFailedEvent.Reset();

        // --- The rest of your code as written in your post ---
    }

    private void DoAsyncWorkFirst()
    {
        try
        {
            for (int i = 0; i < 10000; i++)
            {
                if (workFailedEvent.WaitOne(0, true))
                {
                    break;
                }

                // -- Do some work here ---
            }
        }
        catch (MyException)
        {
            workFailedEvent.Set();
        }
    }

    private void DoAsyncWorkSecond()
    {
        try
        {
            for (int j = 0; j < 20000; j++)
            {
                if (workFailedEvent.WaitOne(0, true))
                {
                    break;
                }
                // --- Do some different work here ---
            }
        }
        catch (MyOtherException)
        {
            workFailedEvent.Set();
        }
    }
}

这里有趣的部分是对WaitOne(0, true)的调用。如果使用超时时间为0,则线程将不会阻塞。由于ManualResetEvent由操作系统同步,因此这个特定的方法调用是一种方便的方式来检查信号,而无需担心竞争条件或实现自己的锁定。


理论上是可以的。在某些情况下,甚至可能更快。但是 volatile 关键字有很多副作用 - 它实际上改变了访问变量的每个块的代码生成方式,而不仅仅是变量本身。除非性能是一个主要问题,否则我通常更喜欢使用锁和同步原语来处理 volatile 字段。 - Aaronaught

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