我认为我现在对逻辑有一些问题。
我使用了阻塞集合来对其他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);
}
}
}
现在我想把这个想象给用户。
用户应该看到
现在我头疼了。
我想要做以下事情:
让网格向用户显示项目。
令人头疼的是如何使这个线程安全,以及哪些部分需要线程安全。
如果我把一些需要花费时间的东西放在workUnit.Add之后,我认为这可能是有可能的,那数据会变得混杂。
这样的事情是否可行?
我需要额外的锁1和2吗?如果是这样,如果等待Take,它会不会完全阻止添加?
是否有一个简单的解决方案或方法可视化,发生了什么?
答案 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)
}
}
在这两个示例中,您甚至不需要任何锁定,您只需将更新发布到正确的上下文(在最后一个甚至不需要明确需要的示例中,编译器会为您处理)