如何可视化阻塞集合

时间:2012-12-13 13:35:09

标签: c# multithreading user-interface

我认为我现在对逻辑有一些问题。

我使用了阻塞集合来对其他PC进行线程安全调用。一般来说,它看起来像这样:

public class MyClass
{
  private BlockingCollection<workUnit> workUnits = new BlockingCollection<workUnit>();

  public void EnQueue(workUnit item)
  {
    workUnits.Add(item);
  }

  private void DeQueue()
  {
    while (!stopFlag)
    {
      workUnit item = workUnits.Take();
      DoLongRunningDBStuff(workUnit);
    }
  }
} 

现在我想把这个想象给用户。

用户应该看到

  1. 项目在队列中
  2. 项目处理已开始
  3. 处理结果(主要是通过/失败/例外)
  4. 现在我头疼了。

    我想要做以下事情:

    让网格向用户显示项目。

    1. 如果项目已入队,请将其添加到工作单元,并将其添加到绑定到数据网格的列表
    2. 如果项目已出列(已消耗),请更新网格列表中的项目。
    3. 令人头疼的是如何使这个线程安全,以及哪些部分需要线程安全。

      如果我把一些需要花费时间的东西放在workUnit.Add之后,我认为这可能是有可能的,那数据会变得混杂。

      这样的事情是否可行?

      1. 如果项目已入队,请将其添加到工作单元,并为UI添加其他BlockingCollection
      2. 如果项目已出列,请在2. BlockingCollection上进行试用并将其删除,更新状态并再次将其附加到第二个列表。
      3. 我需要额外的锁1和2吗?如果是这样,如果等待Take,它会不会完全阻止添加?

        是否有一个简单的解决方案或方法可视化,发生了什么?

1 个答案:

答案 0 :(得分:1)

我会尝试这样做:

public class MyClass
{
  private BlockingCollection<workUnit> workUnits = new BlockingCollection<workUnit>();

  public void EnQueue(workUnit item)
  {
    workUnits.Add(item);
  }

  private void DeQueue()
  {
    while (!stopFlag)
    {
      workUnit item = workUnits.Take();
      item.SetState("Processing Started");
      try
      {
          DoLongRunningDBStuff(workUnit);
          item.SetState("Processing Successful");
      }
      catch
      {
          item.SetState("Processing Failed");
      }
    }
  }
} 

在此示例中,我将workItem.SetState(...)触发事件,它将更新特定项目的UI。但是,因为事件是在非UI线程中引发的,所以它将是事件(显示我将假设的网格的表单)的处理程序,需要将更新发布到上下文中。 UI线程(例如,如果您使用的是WinForms,则可以调用显示数据的控件的Invoke方法。)

在另一个(首选)建议中,我会执行以下操作(如果您可以在.NET 4.0及更高版本中使用TPL):

public class MyClass
{
  public Task EnQueue(workUnit item)
  {
    // Schedule the work on the thread pool. 
    // If you need limited concurrency here, there are schedulers to enable this.
    return Task.Run(() => DoLongRunningDBStuff(item));
  }
} 

如果您使用.NET 4.5,您将能够使用await功能,该功能将自动在UI线程的上下文中同步任务的延续。例如。在调用者方面(假设它是在UI线程上启动),您只需执行以下操作:

private async void btnAddItem_Click(object sender, EventArgs e)
{
    var item = new workUnit();

    // TODO: Add item on UI here

    try
    {
        await myClass.EnQueue(item);

        // TODO: Update UI with success result here (no context synchronisation is needed here it is already in the UI context)
    }
    catch
    {
        // TODO: Update UI with error result here (no context synchronisation is needed here it is already in the UI context)
    }
}

在这两个示例中,您甚至不需要任何锁定,您只需将更新发布到正确的上下文(在最后一个甚至不需要明确需要的示例中,编译器会为您处理)