我的多线程 HttpClient 有任何问题吗?

问题描述 投票:0回答:5

因此,在涉足 Java 和 HttpClient 之后,我决定转向 C#,尝试在提高速度的同时降低内存使用量。我已经阅读了这里的成员提供的大量有关异步与多线程的文章。看来多线程将是更好的方向。

我的程序将访问服务器并一遍又一遍地发送相同的请求,直到检索到 200 代码。原因是因为在高峰时段交通量非常大。它使得服务器非常难以访问并抛出 5xx 错误。代码非常简单,我不想添加循环来帮助观众简化。

我觉得我正朝着正确的方向前进,但我想向社区伸出援手,改掉任何坏习惯。再次感谢您的阅读。

版主注意:我要求检查我的代码是否存在差异,我很清楚多线程 HttpClient 是可以的。

using System;
using System.Net.Http;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Threading;

namespace MF
{
    class MainClass
    {
        public static HttpClient client = new HttpClient();
        static string url = "http://www.website.com";

        public static void getSession() {
            StringContent queryString = new StringContent("{json:here}");

            // Send a request asynchronously continue when complete
            var result = client.PostAsync(new Uri(url), queryString).Result;

            // Check for success or throw exception
            string resultContent = result.Content.ReadAsStringAsync().Result;

            Console.WriteLine(resultContent);
        }
        public static void Run() 
        {
            getSession ();
          // Not yet implemented yet..
          //doMore ();
        }

        public static void Main (string[] args)
        {
            Console.WriteLine ("Welcome!");

            // Create the threads
            ThreadStart threadref1 = new ThreadStart(Run);
            ThreadStart threadref2 = new ThreadStart(Run);
            Console.WriteLine("In the main: Creating the threads...");
            Thread Thread1 = new Thread(threadref1);
            Thread Thread2 = new Thread(threadref1);

            // Start the thread
            Thread1.Start();
            Thread2.Start();
        }
    }
}

另外,我不确定这是否重要,但我正在我的 MacBook Pro 上运行它,并计划在我的 BeagleBoard 上运行它。

c# multithreading httpclient
5个回答
11
投票

如果我是你,我会这样做。我会尽可能地利用异步,因为它比使用线程更有效(您很可能不必一直进行上下文切换,这是昂贵的)。

class MainClass
{
    public static HttpClient client = new HttpClient();
    static string url = "http://www.website.com";

    public static async Task getSessionAsync()
    {
        StringContent queryString = new StringContent("{json:here}");

        // Send a request asynchronously continue when complete
        using (HttpResponseMessage result = await client.PostAsync(url, queryString))
        {
            // Check for success or throw exception
            string resultContent = await result.Content.ReadAsStringAsync();
            Console.WriteLine(resultContent);
        }
    }

    public static async Task RunAsync()
    {
        await getSessionAsync();
        // Not yet implemented yet..
        //doMore ();
    }

    public static void Main(string[] args)
    {
        Console.WriteLine("Welcome!");

        const int parallelRequests = 5;
        // Send the request X times in parallel
        Task.WhenAll(Enumerable.Range(1, parallelRequests).Select(i => RunAsync())).GetAwaiter().GetResult();

        // It would be better to do Task.WhenAny() in a while loop until one of the task succeeds
        // We could add cancellation of other tasks once we get a successful response
    }
}

请注意,我确实同意@Damien_The_Unknowner:如果服务器在重负载下出现问题,您不应该添加不必要的负载(执行X次相同的请求)并导致服务器出现问题。理想情况下,您会修复服务器代码,但我可以理解它不是您的。


5
投票

弄乱标头不是线程安全的。

例如,用新的 OAuth 访问令牌替换 OAuth 访问令牌不是线程安全的。现在使用 Xamarin 移动应用程序来面对这个问题。我有一份崩溃报告,其中一个线程正在修改标头,而另一个线程正在尝试请求。


3
投票

以下方法是线程安全的:

CancelPendingRequests
DeleteAsync
GetAsync
GetByteArrayAsync
GetStreamAsync
GetStringAsync
PostAsync
PutAsync
SendAsync

更多详情:


2
投票

阅读 HttpClient 的文档:

此类型的任何公共静态(在 Visual Basic 中为共享)成员都是线程安全的。不保证任何实例成员都是线程安全的。

不要冒险。每个线程都有一个单独的 HTTP 客户端。

您似乎正在阻塞等待回复的线程,并且您正在从一个不执行任何额外操作的线程中执行此操作。那为什么还要费心async/await呢?您可以使用简单的阻塞调用。

此外 - 您的程序现在在启动线程后立即完成。您可能需要等待线程完成后再从 main 返回。您可以通过程序末尾的这段代码来做到这一点:

Thread1.Join(); Thread2.Join();

根据评论更新:

    决定您想要发出多少个并行请求 - 这将是您的线程数。
  1. 使用 ManualResetEvent 让主线程等待信号。
  2. 在每个线程中不断提交您的请求。一旦您得到正在等待的答案 - 发出 ManualResetEvent 信号并允许您的主函数返回。

0
投票
文档中有

以下注释

DefaultRequestHeaders

 当有未完成的请求时,不应修改。

我正在调试一些不断修改此属性的代码,而没有考虑未完成的请求。这导致了奇怪的异常,堆栈跟踪深入内部

HttpClient

System.NullReferenceException: Object reference not set to an instance of an object. at System.Net.Http.HttpConnection.WriteAsciiStringAsync(String s, Boolean async) at System.Net.Http.HttpConnection.WriteHeadersAsync(HttpHeaders headers, String cookiesFromContainer, Boolean async) at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
正确的修复显然不是在每个请求上修改 

httpClient.DefaultRequestHeaders.Authorization

,而是使用所需的标头实例化 
new HttpRequestMessage

© www.soinside.com 2019 - 2024. All rights reserved.