在 .NET 中同时使用线程和任务。最佳实践

Using Threads and Tasks Concurrently in .NET. Best Practice

我是多线程和并行计算的新手,我的工作任务是将此功能添加到批处理应用程序中。我了解在 C# 中使用线程和任务来完成并行编程的基本原理。关于同时使用线程和任务来完成一组长 运行 进程是否被认为是最佳实践,我需要一些指导。

具体来说,我在我的应用程序中实现了 3 个线程,每个线程向 Web 服务发送 3 个不同的请求 API 并处理返回的结果。一次,一共发送了9个请求。每个请求都由 C# 中的一个任务表示。我在下面包含了一个简单的例子来展示我在我的应用程序中试图完成的事情。如果此实施是最佳做法,请告诉我。谢谢

static void Main(string[] args)
{
    Thread thread1 = new Thread(() =>  Run());
    Thread thread2 = new Thread(() => Run());
    Thread thread3 = new Thread(() => Run());

    thread1.Start();
    thread2.Start();
    thread3.Start();

    thread1.Join();
    thread2.Join();
    thread3.Join();

    Console.ReadKey();
}

private async static void Run()
{
    Task tt1 = WebServiceProcess("Task1");
    Task tt2 = WebServiceProcess("Task2");
    Task tt3 = WebServiceProcess("Task3");

    await Task.WhenAll(tt1, tt2, tt3);
}

private async static Task WebServiceProcess(string taskName)
{
    Console.WriteLine($"Task {taskName} has started.");

    for(int i = 1; i < 10; i++)
    {
        await Task.Delay(100);
        Console.WriteLine($"Task {taskName}: {i}");
    }

    Console.WriteLine($"Task {taskName} has ended.");
}

多线程的第一条规则是不要做多线程:) 这通常是个玩笑,暗示在进行并行处理时可能出现的痛苦和陷阱。这就是创建 TPL(任务并行库)的原因之一,它直接抽象出线程的处理。线程需要很多管理,可能是同步、锁定、阻塞、终止等,TPL 抽象出来并为您处理。

我的建议是在这里查看官方文档: Parallel Programming in .NET 这将带您了解您需要了解的一切。

这些文章附带了很好的示例,展示了在 .NET 上并行编程的最佳实践。

除非您真的别无选择,否则使用原始线程或管理您自己的线程池通常不是一个好的做法。

这不是最佳做法,因为您混合了线程和任务的显式创建。通常在处理任务时您不想控制线程。 Task 在其生命周期中可能使用许多不同的线程,或者只使用一个线程,或者根本 no thread。在大多数情况下,这无论如何都是您无法控制的。您唯一的考虑应该是保护对共享资源的访问,以防您有许多并发的 运行ning 任务改变共享变量或字段或属性。

您的示例代码可以修改为仅使用这样的任务:

static async Task Main(string[] args)
{
    var task1 = Task.Run(RunAsync);
    var task2 = Task.Run(RunAsync);
    var task3 = Task.Run(RunAsync);

    await Task.WhenAll(task1, task2, task3);

    Console.ReadKey();
}

static async Task RunAsync()
{
    Task tt1 = WebServiceProcess("Task1");
    Task tt2 = WebServiceProcess("Task2");
    Task tt3 = WebServiceProcess("Task3");

    await Task.WhenAll(tt1, tt2, tt3);
}

基本上 9 个任务一起启动并且 运行 并发,程序等待它们全部完成,然后再继续下一个命令(Console.ReadKey 行)。将任务分成三组对任务完成没有实际影响。仅当您在方法 RunAsync 内的 await Task.WhenAll 之后有代码时,它才会有所不同,因为当完成一组三人时,此代码将 运行,并且某些组可以比其他组完成得更快.