只是提前提出问题(请不要评论糟糕的架构或如何修改 - 假设这就是它):
- "锁定"使用Invoke / BeginInvoke
时应用语句- 以下代码是否会导致死锁?
醇>
假设我需要在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();
}
}
答案 0 :(得分:2)
锁定一直持续到lock
块,你当前的代码不会导致死锁,但它也无法正常工作。
以下是事件序列:
- 在后台主题中,您调用
MyFunc
。- 对对象
的后台线程进行锁定locker
- 后台线程将“使用AllItems进行一些计算”
- 后台主题调用来自
的AddToArray
传递MyFunc
pitem
DoInGuiThread
- 后台线程从
调用AddToArray
BeginInvoke
后台线程从
DoInGuiThread
调用A
,线程没有阻塞,我将使用B
来表示后台线程 并且BeginInvoke
表示UI线程,这两个都发生在 同一时间。A)
lock (locker)
从它的通话中返回,因为它是非阻塞的 B)UI命中DoInGuiThread
并阻止,因为锁定由。{ 后台主题。- A)
AddToArray
返回。
B)UI仍处于锁定状态,等待后台线程释放锁定。- A)
pitem
返回。
B)UI仍处于锁定状态,等待后台线程释放锁定。- A)后台线程将“使用AllItems的内容更新其他一些结构”(注意,
AllItems
尚未添加到locker
)
B)UI仍处于锁定状态,等待后台线程释放锁定。- A)后台线程释放对象
的锁定locker
的锁定 B)UI线程获取对象MyFunc
- A)
pitem
返回。
B)AllItems
已添加到MyFunc
- A)致电
的锁locker
的人继续运行代码
B)UI线程释放对象MyFunc
- A)致电
醇>AddToArray
的人继续运行代码
B)UI线程返回到消息泵以处理新消息,并且不再被用户“锁定”。
你看到了这个问题吗? MyFunc
会返回,但在AddToArray
结束之前,对象才会添加到数组中,因此Invoke
之后的代码将不会包含数组中的项目。
解决此问题的“通常”方法是使用BeginInvoke
而不是Invoke
,但这会导致发生死锁。这是事件序列,最多6个步骤是相同的,将被跳过。
- 后台线程从
调用DoInGuiThread
Invoke
- A)
lock (locker)
等待B返回消息泵 B)用户界面点击Invoke
并阻止,因为锁是由...持有的 背景线程。- A)
Invoke
等待B返回消息泵 B)UI仍处于锁定状态,等待后台线程释放 锁。- A)
Invoke
等待B返回消息泵 B)UI仍处于锁定状态,等待后台线程释放 锁。- A)
醇>URL url = new URL(urlString); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod("GET"); urlConnection.connect(); // Read the input stream into a String InputStream inputStream = urlConnection.getInputStream(); StringBuffer buffer = new StringBuffer(); if (inputStream == null) { // Nothing to do. return null; } BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { buffer.append(line + "\n"); } if (buffer.length() == 0) { return null; } String stringResult = buffer.toString();
等待B返回消息泵 B)UI仍然被锁定,等待后台线程 释放锁。(这永远重复)
答案 1 :(得分:1)
有两种不同的方式可能会下降。
让我们先解决第一个问题。
在这种情况下,不会有问题。您在MyFunc
中取消锁定,调用AddToArray
调用DoInGuiThread
传递代理人。 DoInGuiThread
会注意到不需要调用并调用委托。在调用AllItems.Add
之前,允许在现在持有锁的同一线程上执行的委托再次进入锁。
这里没问题。
现在,第二种情况,你在其他一些线程上启动这个调用链。
MyFunc
首先获取锁定,调用AddToArray
调用DoInGuiThread
传递委托。由于DoInGuiThread
现在检测到它需要调用它,因此调用传递委托的BeginInvoke
。
此委托通过消息的方式在GUI线程上排队。在这里,事情再次发生分歧。让我们说GUI线程当前很忙,所以它不能在短时间内处理消息(在这种情况下意味着&#34;足以让其余的解释展开&#34; ;)
完成工作的 DoInGuiThread
返回。邮件尚未处理。 DoInGuiThread
返回AddToArray
,现在返回MyFunc
,释放锁定。
当最终处理消息时,没有人拥有锁,因此允许被调用的委托进入锁。
现在,如果消息最终在另一个线程设法完全退出锁之前被处理,那么现在在GUI线程上执行的委托只需要等待。
换句话说,GUI线程会阻塞委托内部,等待锁被释放,因此可以通过代理中的代码输入。