15、从线程返回结果

从线程返回结果

知识点概述

在多线程编程中,经常需要从工作线程获取计算结果。.NET提供了多种方式来实现从线程返回结果:

  • Thread + 共享变量: 通过共享变量或字段存储结果
  • Thread + 回调委托: 使用委托回调传递结果
  • Task: 返回强类型结果的任务(推荐)
  • BackgroundWorker: 提供进度报告和结果返回
  • ThreadPool.QueueUserWorkItem: 配合ManualResetEvent返回结果

代码案例

案例1:使用共享变量返回结果

using System;
using System.Threading;

class SharedVariableResult
{
    private static int _result;
    private static bool _completed = false;
    
    static void Main()
    {
        Thread workerThread = new Thread(CalculateSum);
        workerThread.Start();
        
        // 等待计算完成
        while (!_completed)
        {
            Console.WriteLine("等待计算结果...");
            Thread.Sleep(100);
        }
        
        Console.WriteLine($"计算结果: {_result}");
    }
    
    static void CalculateSum()
    {
        Console.WriteLine("开始计算1到1000的和...");
        
        int sum = 0;
        for (int i = 1; i <= 1000; i++)
        {
            sum += i;
            if (i % 100 == 0) // 模拟较长的计算过程
            {
                Thread.Sleep(50);
            }
        }
        
        _result = sum;
        _completed = true;
        Console.WriteLine("计算完成");
    }
}

案例2:使用回调委托返回结果

using System;
using System.Threading;

class CallbackResult
{
    static void Main()
    {
        Console.WriteLine("启动异步计算...");
        
        // 传递回调委托
        Thread workerThread = new Thread(() => CalculateFactorial(10, OnCalculationComplete));
        workerThread.Start();
        workerThread.Join();
        
        Console.WriteLine("主线程继续执行其他任务...");
        Thread.Sleep(1000);
        Console.WriteLine("程序结束");
    }
    
    static void CalculateFactorial(int number, Action<long> callback)
    {
        Console.WriteLine($"开始计算 {number} 的阶乘...");
        
        long result = 1;
        for (int i = 1; i <= number; i++)
        {
            result *= i;
            Thread.Sleep(100); // 模拟计算延迟
        }
        
        // 通过回调返回结果
        callback(result);
    }
    
    static void OnCalculationComplete(long result)
    {
        Console.WriteLine($"计算完成,结果: {result}");
    }
}

案例3:使用Task返回结果(推荐方式)

using System;
using System.Threading.Tasks;

class TaskResult
{
    static async Task Main()
    {
        Console.WriteLine("启动多个异步计算任务...");
        
        // 启动多个返回结果的任务
        Task<int> sumTask = CalculateSumAsync(1, 100);
        Task<double> averageTask = CalculateAverageAsync(new[] { 1.5, 2.3, 3.7, 4.1, 5.9 });
        Task<string> processTask = ProcessDataAsync("Hello World");
        
        // 等待所有任务完成并获取结果
        int sum = await sumTask;
        double average = await averageTask;
        string processed = await processTask;
        
        Console.WriteLine($"求和结果: {sum}");
        Console.WriteLine($"平均值: {average:F2}");
        Console.WriteLine($"处理结果: {processed}");
    }
    
    static Task<int> CalculateSumAsync(int start, int end)
    {
        return Task.Run(() =>
        {
            Console.WriteLine($"计算 {start} 到 {end} 的和...");
            int sum = 0;
            for (int i = start; i <= end; i++)
            {
                sum += i;
            }
            Console.WriteLine("求和计算完成");
            return sum;
        });
    }
    
    static Task<double> CalculateAverageAsync(double[] numbers)
    {
        return Task.Run(() =>
        {
            Console.WriteLine("计算数组平均值...");
            double sum = 0;
            foreach (double num in numbers)
            {
                sum += num;
            }
            Console.WriteLine("平均值计算完成");
            return sum / numbers.Length;
        });
    }
    
    static Task<string> ProcessDataAsync(string input)
    {
        return Task.Run(() =>
        {
            Console.WriteLine("处理字符串数据...");
            // 模拟数据处理
            string result = input.ToUpper().Replace(" ", "_");
            Console.WriteLine("字符串处理完成");
            return result;
        });
    }
}

案例4:使用ManualResetEvent同步结果

using System;
using System.Threading;

class ManualResetEventResult
{
    private static int _result;
    private static ManualResetEvent _resetEvent = new ManualResetEvent(false);
    
    static void Main()
    {
        Console.WriteLine("启动后台计算...");
        
        ThreadPool.QueueUserWorkItem(PerformCalculation);
        
        Console.WriteLine("主线程执行其他任务...");
        // 模拟主线程的其他工作
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine($"主线程任务 {i + 1}");
            Thread.Sleep(500);
        }
        
        Console.WriteLine("等待计算结果...");
        _resetEvent.WaitOne(); // 等待计算完成
        
        Console.WriteLine($"最终结果: {_result}");
    }
    
    static void PerformCalculation(object state)
    {
        Console.WriteLine("后台线程开始计算...");
        
        // 模拟复杂计算
        int result = 0;
        for (int i = 1; i <= 50; i++)
        {
            result += i * i;
            Thread.Sleep(50); // 模拟计算时间
        }
        
        _result = result;
        Console.WriteLine("后台计算完成");
        
        // 通知主线程计算完成
        _resetEvent.Set();
    }
}

案例5:复杂场景:多个线程协作返回结果

using System;
using System.Threading;
using System.Collections.Concurrent;

class MultiThreadCooperation
{
    private static ConcurrentQueue<int> _results = new ConcurrentQueue<int>();
    private static CountdownEvent _countdown = new CountdownEvent(3);
    
    static void Main()
    {
        Console.WriteLine("启动多线程协作计算...");
        
        // 启动多个工作线程
        for (int i = 0; i < 3; i++)
        {
            int workerId = i + 1;
            Thread worker = new Thread(() => WorkerTask(workerId));
            worker.Name = $"Worker-{workerId}";
            worker.Start();
        }
        
        // 等待所有工作线程完成
        _countdown.Wait();
        
        // 收集并处理所有结果
        Console.WriteLine("\n所有工作线程完成,收集结果:");
        int totalResult = 0;
        while (_results.TryDequeue(out int result))
        {
            Console.WriteLine($"获取到结果: {result}");
            totalResult += result;
        }
        
        Console.WriteLine($"最终汇总结果: {totalResult}");
    }
    
    static void WorkerTask(int workerId)
    {
        Console.WriteLine($"{Thread.CurrentThread.Name} 开始工作");
        
        // 每个工作线程计算不同的值
        int result = workerId * workerId * 10;
        
        // 模拟工作时间
        Thread.Sleep(1000 + workerId * 300);
        
        // 将结果添加到队列
        _results.Enqueue(result);
        Console.WriteLine($"{Thread.CurrentThread.Name} 完成,结果: {result}");
        
        // 通知完成
        _countdown.Signal();
    }
}

知识点总结

  1. 共享变量方式: 简单直接,但需要注意线程安全和同步问题
  2. 回调委托: 提供了更好的封装,支持异步通知机制
  3. Task: 现代推荐方式,类型安全,支持异步/等待模式
  4. 同步原语: ManualResetEvent、CountdownEvent等用于线程间同步
  5. 并发集合: ConcurrentQueue等提供线程安全的数据交换
  6. 错误处理: 需要考虑线程执行中的异常处理和结果验证
  7. 性能考虑: 选择合适的方式平衡代码复杂度和执行效率
posted @ 2025-08-27 01:24  jessqiu  阅读(29)  评论(0)    收藏  举报