与循环有关的后台线程使用

假定需要按顺序处理一系统工作,它们可能消耗较多时间,GUI线程期望获取每件工作的结果并予显示,同时处理掉异常而不是中断整体工作,比如使用同一个Http接口以上传文件,并发访问将会报错。

常规做法是使用一个后台线程,线程遍历工作集合,并把结果POST到主线程,逻辑如下:

AsyncOperation asyncOperation = null;

private void button1_Click(object sender, EventArgs e)
{
    asyncOperation = AsyncOperationManager.CreateOperation(null);
    Thread thread = new Thread(loopWork);
    thread.IsBackground = true;
    List<int> arr = Enumerable.Range(0, 10).ToList();
    thread.Start(arr);
}

private void loopWork(object obj)
{
    List<int> arr = obj as List<int>;
    foreach (var item in arr)
    {
        try
        {
            string result = doWork(item);    //do some job
            asyncOperation.Post(showResult, result); //show result
        }
        catch (Exception ex)
        {
            asyncOperation.Post(showError, ex.ToString());    //handle error
        }
    }
}

doWork、showResult、showError等方法不再列出。这些方法都是接收object类型参数的void类型(即Action<object>)委托的实例,示例使用AsyncOperation.Post()方法实质上使用了Form窗体的同步上下文,更多内容请查阅CSDN。

Task引入后任务调度器TaskScheduler的使用,同样是对同步上下文的使用,结果返回与异常处理不再分散到类的各种方法,避免了各种装箱拆箱。此例需求复杂了点,实现如下:

private void button2_Click(object sender, EventArgs e)
{
    var task = new Task(() => { });
    ConcurrentQueue<int> queue = new ConcurrentQueue<int>(Enumerable.Range(0, 10));
    while (!queue.IsEmpty)
    {
        int item;
        if (queue.TryDequeue(out item))
        {
            task.ContinueWith(t => doWork(item), TaskContinuationOptions.ExecuteSynchronously)
                .ContinueWith(t =>
                {
                    if (t.Exception == null)
                    {
                        Console.WriteLine(t.Result);
                    }
                    else
                    {
                        MessageBox.Show(t.Exception.GetBaseException().ToString());
                    }
                }, TaskScheduler.FromCurrentSynchronizationContext());
        }
        else
        {
            Console.WriteLine("TryDequeue failed");
        }
    }
    task.Start();
}

ConcurrentQueue<T>是先进先出的线程安全的集合,使用非常简单,相关概念仍请查阅CSDN。TaskContinuationOptions.ExecuteSynchronously保证了任务的排队顺序。

逻辑是先创建一个什么也不干的任务,然后遍历集合,为该任务顺序添加工作,并在工作完成后处理结果与异常。

示例已经过测试。

原文地址:https://www.cnblogs.com/Jusfr/p/2638169.html