BackgroundWorker 限制工人数量并在可用时创建它们

SOf*_*tic 3 c# multithreading backgroundworker

我有一个BackgroundWorker为列表中的每个对象创建一个的函数,我现在想要做的是将创建的工作人员数量限制为 20,并在他们完成后处理列表中的下一项。我正在考虑使用队列,并在处理工作人员之前检查队列大小是否小于 20,然后创建工作人员并将其添加到队列中。我的问题是如何在完成后从队列中删除该项目?以及如何设置循环以等待工作人员可用?

编辑 我想我已经接近了,这就是我现在所拥有的,但问题是程序卡在了while循环中:

var myObjectList = new List<myObject>();
myObjectList = PopulateList();
BackgroundWorker bgw;
foreach (var obj in myObjectList)
{
    bgw = new BackgroundWorker();
    while(BgwList.Count >= 20); //getting stuck here, why?
    BgwList.Add(bgw);
    if(!bgw.IsBusy)
    {
        bgw.RunWorkerAsync(obj);
    }
}
Run Code Online (Sandbox Code Playgroud)

BackgroundWorker完成后删除:

void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    BgwList.Remove((BackgroundWorker)sender); //verified that bgw is being removed from list
    //... other code
}
Run Code Online (Sandbox Code Playgroud)

Dre*_*rsh 5

诚实地?不要使用BackgroundWorker. 如果您想要这种控制,请手动使用 TPL、TPL 数据流甚至反应式扩展(Rx)。

就我个人而言,我会使用 TPL 数据流,我会设置一个TransformBlock<MyObject, MyObject>您配置的MaxDegreeOfParallelism对您有意义的(例如,您想一次处理 20 个),然后将其链接回ActionBlock<MyObject>配置为在 UI 上运行的 UI 更新(Dispatcher) 线。这样的代码看起来像这样......

在初始化逻辑的某个地方,可能作为 Window 构造函数的一部分(注意:必须在主 Dispatcher 线程上执行才能正常工作)

TransformBlock<MyObject, MyObject> myProcessingBlock = new ActionBlock<MyObject, MyObject>(
   myObject =>
   {
     // ... perform your processing of this object here ...

     return myObject;
   },
   new ExecutionDataflowBlockOptions
   {
      MaxDegreeOfParallelism = 20
   });

ActionBlock<MyObject> myUINotificationBlock = new ActionBlock<MyObject>(
   myObject =>
   {
       // ... update the UI details for this data here ...
   },
   new ExecutionDataflowBlockOptions
   {
       TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext() // must be executed on the Dispatcher block!
   });

 myProcessingBlock.LinkTo(myUINotificationBlock);
Run Code Online (Sandbox Code Playgroud)

这就是您将新作品发布到它的方式:

MyObject someObjectToProcess = GetSomeObjectToProcess(...);

myProcessingBlock.Post(someObjectToProcess);
Run Code Online (Sandbox Code Playgroud)

剩下的一切都由 TPL 数据流库的魔力为您处理。只需声明并释放它即可。TPL Dataflow 甚至支持async方法,因此,如果您知道要进行 Web 服务调用,则只需async在您定义的方法上使用关键字即可,TransformBlock<MyObject, MyObject>如下所示:

TransformBlock<MyObject, MyObject> myProcessingBlock = new ActionBlock<MyObject, MyObject>(
   async myObject =>
   {
     HttpClient someHttpClient = new HttpClient();

     HttpResponseMessage responseMessage = await someHttpClient.PostAsync(..., ...);

     return myObject;
   },
   new ExecutionDataflowBlockOptions
   {
      MaxDegreeOfParallelism = 20
   });
Run Code Online (Sandbox Code Playgroud)

这样你甚至不会在HTTP网络调用未完成的情况下阻塞CPU线程,这更加充满胜利。