EDIT2 - > 看看底部; 的< - EDIT2
我遇到了(甚至至少对我而言)行为。
我甚至创建了简单的WinForms类和简单的类(下面的代码)来测试它。
我一直认为,如果之前的lock(m_lock)
调用没有结束,则调用lock(m_lock)
,第一个将等待并输入onece,第二个离开锁定范围。不。
行动的流程是:
创建Class1
对象;
调用Start()
方法;
在DoSomething()
方法锁定m_lock
时调用run
方法;
输出是:
开始()
尝试获取锁定
获得锁定
发布锁定
尝试获取锁定
获得锁定
DoSomething()尝试获取锁
......挂起......
我错过了什么或做错了什么?我是C#的新手(来自C ++)所以也许C#中有一些 gotchas 。
它仍然挂起......(当我写完这篇文章的时候)
修改 - >
在现实世界中,我使用锁来保护serialPort上的读/写/配置(具有同步读/写,而不是异步读/写)。我在dbg中看到有一些内部WaitOne
调用。不知道它是否相关。
的< - 修改
以下是示例:
using System;
namespace LockTester
{
public class Class1
{
object m_lock = null;
bool m_isRunning;
System.Threading.Thread m_thread = null;
public Class1()
{
Console.WriteLine("Class1 ctor");
m_lock = new object();
m_isRunning = false;
}
public void DoSomething(){
Console.WriteLine("DoSomething() Trying to acquire lock");
lock(m_lock){
Console.WriteLine("DoSomething() Acquired lock");
}
Console.WriteLine("DoSomething() Released lock");
}
public void Start(){
Console.WriteLine("start()");
m_isRunning = true;
if (m_thread == null){
m_thread = new System.Threading.Thread(Run);
}
m_thread.Start();
}
public void Stop(){
Console.WriteLine("stop()");
m_isRunning = false;
}
private void Run(){
while (m_isRunning){
Console.WriteLine("Trying to acquire lock");
lock(m_lock){
Console.WriteLine("Acquired lock");
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine("Released lock");
System.Threading.Thread.Sleep(1000);
}
}
}
}
EDIT2:
好的,找到了答案。这是一个更常见的分母。
我找到了某个地方(很可能是)一个将控制台输出重定向到TextBox的解决方案(纯粹是测试原因,你知道 - 使用gui的小型测试应用程序,它可以捕获被测试对象的内部消息打印到控制台)。
以下是代码:
在我的表单构造函数中使用:
_writer = new TextBoxStreamWriter(textBox1, this);
Console.SetOut(_writer);
public class TextBoxStreamWriter : TextWriter
{
TextBox _output = null;
Form _form = null;
object _lock = new object();
delegate void SetTextCallback(string text);
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (_output.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
_form.Invoke(d, new object[] { text });
}
else
{
_output.AppendText(text);
}
}
public TextBoxStreamWriter(TextBox output, Form form)
{
_output = output;
_form = form;
}
public override void Write(char value)
{
lock (_lock)
{
base.Write(value);
SetText(value.ToString());
}
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
任何人都可以解释为什么会导致这个问题?
答案 0 :(得分:2)
当您致电Form.Invoke时,它会执行此操作:
在拥有控件底层窗口句柄的线程上执行指定的委托。
这样做的方法是将消息发布到拥有线程的消息队列中,并等待该线程处理该消息。
因此,Invoke
是阻止调用,在调用被调用的委托之前不会返回。
现在,您的代码阻塞的可能原因是您的主GUI线程已经在等待其他事情发生,可能是您的外部程序已经完成。
因此它实际上并没有处理消息。
如果这是原因,那么这里的解决方案是删除GUI线程的阻塞部分。不要坐在那里等待外部程序完成,而是转出等待它完成的任务,然后在主窗体上引发适当的事件。同时,主线程可以自由处理消息,更新文本框等。
请注意,这意味着如果启动外部程序是为了响应事件(如按钮单击),则可能需要在程序运行时禁用部分用户界面,以避免用户单击按钮两次,开始两次并行执行,都会报告到同一个文本框。
结论:多线程编程很难!