0

我想我现在的逻辑有些问题。

我使用阻塞集合对其他 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. 处理结果(主要是通过/失败/异常)

现在我有些头疼。

我正在考虑执行以下操作:

有一个网格来向用户显示项目。

  1. 如果 item 已入队,则将其添加到 workunits 并另外添加到绑定到 datagrid 的列表中
  2. 如果项目已出列(已使用),则更新网格列表中的项目。

令人头疼的是,如何使这个线程安全,以及需要哪些部分是线程安全的。

如果我把一些需要时间的东西放在 workUnit.Add 后面,我认为这是可能的,那么数据就会混合在一起。

这样的事情可行吗?

  1. 如果项目已入队,则将其添加到工作单元和 UI 的附加 BlockingCollection
  2. 如果项目已出列,请尝试 2​​.BlockingCollection 并将其删除,更新状态并再次将其附加到第二个列表。

我需要在 1 和 2 附近加锁吗?如果是这样,如果等待Take,它不会完全阻止添加吗?

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

4

1 回答 1

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)
    }
}

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

于 2012-12-14T09:06:07.307 回答