首页 > 解决方案 > 如何在指定线程中运行异步方法

问题描述

我有一个需要的应用程序(http web load test app)new Thread() ,并且HttpClient唯一有异步方法,那么我该如何同步运行动作

ps:我尝试使用full Task,但它使用的线程数很低(仅限30个线程),所以我想试试看Thread它是否可以更快。会 .GetAwaiter().GetResult()花费2个线程(100个线程变成200个线程)吗?


以前我用

for(var i = 0; i< 200;i++)
{
    Task.Run(async ()=>
    {
        while(thereStillHaveRequestToMake)
        {
        await httpclient.SendAsync() // some thing like this
        }
    });
}

// the prolem is there are only 30-40 Thread in use (From TaskManager)

所以想直接改用Thread

for(var i = 0; i< 200;i++)
{
    new Thread(()=>
    {
        while(thereStillHaveRequestToMake)
        {
            httpclient.SendAsync().GetAwaiter.GetResult()
        }
    });
}

标签: c#multithreadingasynchronous

解决方案


我有一个需要 new Thread() 的应用程序(http web 负载测试应用程序)

为什么?

HttpClient 只有异步方法,所以我如何同步运行动作

为什么。

或者如何在 C# 中从同步方法调用异步方法?.

我尝试使用完整的任务,但它使用的线程数很低(仅限 30 个线程),

任务不是线程。我们可以通过在线程池上运行方法来轻松测试这一点。首先,我们将 ThreadPool 设置为只允许单个线程。

class Program
{
  private const int MaxThreads = 1;

  static void Main(string[] args)
  {
    ThreadPool.SetMinThreads(MaxThreads, 1);
    Console.WriteLine(ThreadPool.SetMaxThreads(MaxThreads, 1));
    Task.Run(() => SomeMethod(new StateInfo { Order = 0, WaitFor = 3000 }));
    Task.Run(() => SomeMethod(new StateInfo { Order = 1, WaitFor = 3000 }));
    Task.Run(() => SomeMethod(new StateInfo { Order = 2, WaitFor = 3000 }));
    Console.WriteLine("Main thread does some work, then sleeps.");
    Thread.Sleep(5000);
    Console.WriteLine("Main thread exits.");
  }

  static void SomeMethod(Object stateInfo)
  {
    var si = (StateInfo)stateInfo;
    Console.WriteLine($"Hello from the thread pool. {si.Order}");
    Thread.Sleep(si.WaitFor);
  }

  public class StateInfo
  {
    public int Order { get; set; }
    public int WaitFor { get; set; }
  }
}

输出

真的

主线程做一些工作,然后休眠。

来自线程池的你好。1

来自线程池的你好。2

主线程退出。

由于我们有 1 个线程,并且我们告诉前两个方法总共等待 6 秒,但主线程在 5 秒后退出,我们永远不会收到来自第 3 个方法的消息。我们可以很容易地通过改变MaxThreads = 2which 来测试它,它会产生如下的结果(我们得到 3 个结果,但不一定按顺序排列):

真的

主线程做一些工作,然后休眠。

来自线程池的你好。1

来自线程池的你好。2

来自线程池的你好。3

主线程退出。

现在我们已经保证我们使用的是单个线程,让我们看看我们可以同时同步处理多少个请求。

static void SomeMethod(Object stateInfo)
{
  var si = (StateInfo)stateInfo;
  Console.WriteLine($"Hello from the thread pool. {si.Order}");
  httpClient.GetStringAsync($"https://www.google.com");
  Console.WriteLine($"Hello from the thread pool. {si.Order} finished");
}

由于我们不是异步/等待请求,因此它同步运行,因此输出是可预测的:

真的

主线程做一些工作,然后休眠。

来自线程池的你好。1

来自线程池的你好。1 完成

来自线程池的你好。2

来自线程池的你好。2 完成

来自线程池的你好。3

来自线程池的你好。3 完成

主线程退出。

这并没有真正进行任何负载测试,因为同步调用会等到前一个调用完成。为了进行负载测试,我们需要许多并发调用。这很容易通过使用异步等待的单个线程完成。

更新方法:

static async Task SomeMethod(Object stateInfo)
{
  var si = (StateInfo)stateInfo;
  Console.WriteLine($"Hello from the thread pool. {si.Order}");
  await httpClient.GetStringAsync($"https://www.google.com");
  Console.WriteLine($"Hello from the thread pool. {si.Order} finished");
}

使用 linq 创建请求列表,然后等待所有请求完成。

static void Main(string[] args)
{
  ThreadPool.SetMinThreads(MaxThreads, 1);
  Console.WriteLine(ThreadPool.SetMaxThreads(MaxThreads, 1));

  Console.WriteLine("Start Requests");
  var requests = Enumerable.Range(0, 200)
    .Select(async (x) => await Task.Run(() => SomeMethod2(new StateInfo { Order = x, WaitFor = 0 })))
    .ToArray();

  Console.WriteLine("Wait for them.");
  Task.WaitAll(requests.ToArray());

  Console.WriteLine("Main thread exits.");
  Console.ReadKey();
}

产量(我不想在这里放 400 行代码)

真的

开始请求

等他们。

来自线程池的你好。0

来自线程池的你好。1

来自线程池的你好。2

.... 重复

来自线程池的你好。199

来自线程池的你好。178 完成

来自线程池的你好。5 完成

来自线程池的你好。3 完成

来自线程池的你好。15 完成

来自线程池的你好。26 完成

来自线程池的你好。4 完成

.... 重复直到所有 200 个请求都完成

主线程退出。

单个线程上的 200 个 Http 请求。为什么需要更多线程?


推荐阅读