Just to put the questions upfront (please no comments about the bad architecture or how to revise - suppose this is what it is):
- How does the "lock" statement apply when using Invoke/BeginInvoke
- Could the following code result in a deadlock?
假设我有以下需要在 GUI 线程上更新的 BindingList:
var AllItems = new BindingList<Item>();
我想确保对它的所有更新都是同步的。 假设我有以下子例程来进行一些计算,然后将一个新条目插入到 BindingList 中:
private void MyFunc() {
lock(locker) {
... //do some calculations with AllItems
AddToArray(new Item(pos.ItemNo));
... //update some other structures with the contents of AllItems
}
}
AddToArray 看起来像:
private void AddToArray (Item pitem)
{
DoInGuiThread(() =>
{
lock (locker)
{
AllItems.Add(pitem);
}
});
}
DoInGuiThread 看起来像:
private void DoInGuiThread(Action action) {
if(InvokeRequired) {
BeginInvoke(action);
} else {
action.Invoke();
}
}
请您参考如下方法:
锁一直保持到您离开 lock block ,您当前的代码不会导致死锁,但它也无法正常工作。
这是事件的顺序:
- On a background thread you call
MyFunc.- A lock is taken for the background thread for the object
locker- The background thread will "do some calculations with AllItems"
- The background thread calls
AddToArrayfromMyFuncpassing inpitem- The background thread calls
DoInGuiThreadfromAddToArrayThe background thread calls
BeginInvokefromDoInGuiThread, the thread does not block, I will useAto signify the background thread andBto signify the UI thread, both of these are happening at the same time.A)
BeginInvokereturns from it's call because it is non blocking.
B) The UI hitslock (locker)and blocks because the lock is held by the background thread.- A)
DoInGuiThreadreturns.
B) The UI is still locked up, waiting for the background thread to release the lock.- A)
AddToArrayreturns.
B) The UI is still locked up, waiting for the background thread to release the lock.- A) The background thread will "update some other structures with the contents of AllItems" (note,
pitemhas not yet been added toAllItems)
B) The UI is still locked up, waiting for the background thread to release the lock.- A) The background thread releases the lock for the object
locker
B) The UI thread takes the lock for the objectlocker- A)
MyFuncreturns.
B)pitemis added toAllItems- A) Whoever called
MyFunccontinues to run code
B) The UI thread releases the lock for the objectlocker- A) Whoever called
MyFunccontinues to run code
B) The UI thread returns to the message pump to process new messages and no longer appears to be "locked up" by the user.
你看到问题了吗? AddToArray 返回,但该对象直到 MyFunc 结束后才添加到数组中,因此您在 AddToArray 之后的代码不会在数组中包含该项目.
解决此问题的“通常”方法是使用 Invoke 而不是 BeginInvoke 但这会导致死锁发生。这是事件的顺序,最多 6 步是相同的,将被跳过。
- The background thread calls
InvokefromDoInGuiThread- A)
Invokewaits for B to return to the message pump.
B) The UI hitslock (locker)and blocks because the lock is held by the background thread.- A)
Invokewaits for B to return to the message pump.
B) The UI is still locked up, waiting for the background thread to release the lock.- A)
Invokewaits for B to return to the message pump.
B) The UI is still locked up, waiting for the background thread to release the lock.- A)
Invokewaits for B to return to the message pump.
B) The UI is still locked up, waiting for the background thread to release the lock.(This repeats forever)
