C# 线程池 HttpWebRequest

发布于 2024-08-04 06:24:48 字数 945 浏览 3 评论 0原文

我已经阅读并查看了相当多的线程池示例,但我似乎无法按照我需要的方式理解它。我设法得到的工作并不是我真正需要的。它只是在自己的线程中运行该函数。

public static void Main()
    {
        while (true)
        {
            try
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(Process));
                Console.WriteLine("ID has been queued for fetching");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }
            Console.ReadLine();
        }
    }

public static void Process(object state)
{

    var s = StatsFecther("byId", "0"); //returns all player stats
    Console.WriteLine("Account: " + s.nickname);
    Console.WriteLine("ID: " + s.account_id);
    Console.ReadLine();
}

我想做的是让大约 50 个线程(也许更多)来获取包含玩家统计信息的序列化 php 数据。从用户 0 开始,一直到我指定的用户 ID (300,000)。我的问题不是如何获取统计数据,我知道如何获取统计数据并读取它们,而是如何编写一个线程池,它将继续获取统计数据,直到达到第 300,000 个用户 ID,而不会踩到其他线程的脚趾,将统计数据检索到数据库时将其保存。

I've read and looked a quite a few examples for Threadpooling but I just cant seem to understand it they way I need to. What I have manage to get working is not really what I need. It just runs the function in its own thread.

public static void Main()
    {
        while (true)
        {
            try
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(Process));
                Console.WriteLine("ID has been queued for fetching");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }
            Console.ReadLine();
        }
    }

public static void Process(object state)
{

    var s = StatsFecther("byId", "0"); //returns all player stats
    Console.WriteLine("Account: " + s.nickname);
    Console.WriteLine("ID: " + s.account_id);
    Console.ReadLine();
}

What I'm trying to do is have about 50 threads going (maybe more) that fetch serialized php data containing player stats. Starting from user 0 all the way up to a user ID i specify (300,000). My question is not about how to fetch the stats I know how to get the stats and read them, But how I write a Threadpool that will keep fetching stats till it gets to 300,000th user ID without stepping on the toes of the other threads and saves the stats as it retrieves them to a Database.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

溺渁∝ 2024-08-11 06:24:48
static int _globalId = 0;
public static void Process(object state)
{    
  // each queued Process call gets its own player ID to fetch
  processId = InterlockedIncrement(ref _globalId); 
  var s = StatsFecther("byId", processId); //returns all player stats 

  Console.WriteLine("Account: " + s.nickname);    
  Console.WriteLine("ID: " + s.account_id);    
  Console.ReadLine();
}

这是最简单的事情。但远非最佳。您正在使用同步调用,您依赖 ThreadPool 来限制调用率,您没有针对失败调用的重试策略,并且您的应用程序在错误情况下(当 Web 调用失败时)将表现得非常糟糕。

首先,您应该考虑使用 WebRequest 的异步方法: BeginGetRequestStream (如果您 POST 并有请求正文)和/或 BeginGetResponse。这些方法可以更好地扩展,并且您将以更少的 CPU 获得更高的吞吐量(当然,如果后端可以跟上)。

其次,你应该考虑自我限制。在一个类似的项目中,我使用了待处理的请求计数。成功后,每个调用将再提交 2 个调用,并以限制计数为上限。如果失败,调用将不会提交任何内容。如果没有待处理的呼叫,则基于计时器的重试每分钟提交一个新呼叫。这样,当服务关闭时,您每分钟只尝试一次,从而节省了自己的资源,避免在没有牵引力的情况下旋转,并且当服务启动时,您可以将吞吐量增加到限制上限。

您还应该知道.Net 框架将限制它对任何资源进行的并发连接的数量。您必须找到您的目的地 ServicePoint 并更改ConnectionLimit 的默认值 (2 )到您愿意限制的最大值。

关于数据库更新部分,有很多变量在起作用,而且信息太少,无法提供任何有意义的建议。一些一般建议是在数据库调用中也使用异步方法,调整 yoru 连接池的大小以允许您的限制上限,确保您的更新使用玩家 ID 作为密钥,这样就不会在从不同线程更新相同记录时陷入死锁。

static int _globalId = 0;
public static void Process(object state)
{    
  // each queued Process call gets its own player ID to fetch
  processId = InterlockedIncrement(ref _globalId); 
  var s = StatsFecther("byId", processId); //returns all player stats 

  Console.WriteLine("Account: " + s.nickname);    
  Console.WriteLine("ID: " + s.account_id);    
  Console.ReadLine();
}

This is the simplest thing to do. But is far from optimal. You are using synchronous calls, you are relying on the ThreadPool to throttle your call rate, you have no retry policy for failed calls and your application will behave extremly bad under error conditions (when the web calls are failing).

First you should consider using the async methods of WebRequest: BeginGetRequestStream (if you POST and have a request body) and/or BeginGetResponse. These methods scale much better and you'll get a higher troughput for less CPU (if the back end can keep up of course).

Second you should consider self-throthling. On a similar project I used a pending request count. On success, each call would submit 2 more calls, capped with the throtling count. On failure the call would not submit anything. If no calls are pending, a timer based retry submits a new call every minute. This way you only attempt once per minute when the service is down, saving your own resources from spinning w/o traction, and you increase the throughput back up to the throtling cap when the service is up.

You should also know that the .Net framework will limit the number of concurent conncetions it makes to any resource. You must find your destination ServicePoint and change the ConnectionLimit from its default value (2) to the max value you are willing to throttle on.

About the database update part, there are way to many variables at play and way too little information to give any meaningfull advice. Some general advice would be use asynchronous methods in the database call also, size yoru conneciton pool to allow for your throtling cap, make sure your updates use the player ID as a key so you don't deadlock on updating the same record from different threads.

时光瘦了 2024-08-11 06:24:48

如何确定用户 ID?一种选择是将所有线程分段,以便线程 X 处理 0 - N 之间的 ID,依此类推,作为您拥有的线程数的一小部分。

How do you determine the user ID? One option is to segment all the threads so that thread X deals with ID's from 0 - N, and so on, as a fraction of how many threads you have.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文