Skip to content

C# 多线程 03- 使用线程池 01- 在线程池中调用委托

🏷️ 《C# 多线程》

在线程池中调用委托

异步编程模型(Asynchronous Programming Model,简称 APM),这是 .net 历史中的第一个异步编程模式。

点击查看代码
csharp
private delegate string RunOnThreadPool(out int threadId);

static void Main(string[] args)
{
    int threadId = 0;

    RunOnThreadPool poolDelegate = Test;

    // 不使用异步处理
    var t = new Thread(() => Test(out threadId));
    t.Start();
    // 等待 t 结束
    t.Join();
    // 打印结果
    Console.WriteLine($"线程 Id:{threadId}");

    // 开始异步处理,并指定回调函数
    IAsyncResult r = poolDelegate.BeginInvoke(out threadId, Callback, "一个代理异步调用");
    // 等待 poolDelegate 执行结束
    r.AsyncWaitHandle.WaitOne();
    // 获取异步处理结果
    // 虽然 threadId 是按址传参,但也必须通过 EndInvoke 获取返回结果
    string result = poolDelegate.EndInvoke(out threadId, r);
    // 打印结果
    Console.WriteLine($"线程池工作线程 Id:{threadId}");
    Console.WriteLine(result);
    // 这里的延迟是为了给回调函数足够的执行时间
    Thread.Sleep(TimeSpan.FromSeconds(2));
}

private static void Callback(IAsyncResult ar)
{
    Console.WriteLine("开始一个回调");
    // AsyncState 值为 BeginInvoke 的第三个参数值
    Console.WriteLine($"回调状态:{ar.AsyncState}");
    Console.WriteLine($"是否为线程池中的线程:{Thread.CurrentThread.IsThreadPoolThread}"); // true
    // 回调函数的线程 ID 和异步处理的线程 ID 是相同的
    Console.WriteLine($"线程池工作线程 Id:{Thread.CurrentThread.ManagedThreadId}");
}

private static string Test(out int threadId)
{
    Console.WriteLine("开始...");
    Console.WriteLine($"是否为线程池中的线程:{Thread.CurrentThread.IsThreadPoolThread}");
    Thread.Sleep(TimeSpan.FromSeconds(2));
    threadId = Thread.CurrentThread.ManagedThreadId;
    // 返回值可以通过代理的 EndInvoke 方法获取
    return $"线程池工作线程 Id 是 {Thread.CurrentThread.ManagedThreadId}";
}

打印结果

点击查看执行结果
开始...
是否为线程池中的线程:False
线程Id:3
开始...
是否为线程池中的线程:True
线程池工作线程Id:4
线程池工作线程Id是 4
开始一个回调
回调状态:一个代理异步调用
是否为线程池中的线程:True
线程池工作线程Id:4

例子中使用 r.AsyncWaitHandle.WaitOne() 方法等待操作完成,但这不是必要的。

将该代码注释掉,处理仍然可以正常执行。因为 EndInvoke 方法会等待异步操作完成

调用 EndInvoke 方法是非常重要的,因为该方法会将任何未处理的异常抛回到调用线程中。