如何在指定线程中运行异步方法

时间:2019-01-22 06:07:52

标签: c# multithreading asynchronous

我有一个需要new Thread()的应用程序(http Web负载测试应用程序),而HttpClient仅具有异步方法,所以我该如何同步运行操作

ps:我尝试使用完整的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()
        }
    });
}

1 个答案:

答案 0 :(得分:2)

  

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

为什么?

  

HttpClient仅具有异步方法,因此如何同步运行操作

为什么。

How to call asynchronous method from synchronous method in C#?

  

我尝试使用完整的Task,但是它使用的线程数很低(仅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 = 2来轻松地测试它,其结果如下所示(我们得到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");
}

由于我们不是 async / await 请求,因此它会同步运行 ,因此输出可以预测:

  

     

主线程完成一些工作,然后进入睡眠状态。

     

您好,来自线程池。 1

     

您好,来自线程池。 1个完成

     

您好,来自线程池。 2

     

您好,来自线程池。 2完成

     

您好,来自线程池。 3

     

您好,来自线程池。 3完成

     

主线程退出。

这实际上并不会进行任何负载测试,因为 synchronous 调用要等到前一个完成为止。为了进行负载测试,我们需要许多 concurrent 调用。这可以通过使用异步等待的单个线程轻松完成。

更新方法:

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请求。为什么需要更多线程?