-1

EDIT2--> 看看底部; <--EDIT2

我遇到了奇怪的(至少对我来说)行为。

我什至创建了简单的 WinForms 类和简单的类(下面的代码)来测试它。

我一直认为,lock(m_lock)如果之前lock(m_lock)的调用没有结束,那么第一个调用会等待并进入一次,第二个会离开锁定范围。没有。

动作流程是:

  1. 创建Class1对象;

  2. 调用Start()方法;

  3. 在方法被锁定时调用DoSomething()方法;m_lockrun

输出是:

开始()

试图获取锁

获得锁

释放锁

试图获取锁

获得锁

DoSomething() 试图获取锁

……挂了……

我错过了什么或做错了什么?我是 C# 的新手(来自 C++)所以也许有一些陷阱

它仍然挂起......(当我写完这篇文章时)

编辑--> 在现实世界中,我使用锁来保护 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);
            }
        }
    }
}

编辑2:

好的,找到答案了。这是另一个共同点。

我在某处(可能)找到了将控制台输出重定向到 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; }
    }
}

任何人都可以解释为什么这会导致这个问题?

4

1 回答 1

2

当您调用Form.Invoke时,它​​将执行以下操作:

在拥有控件的基础窗口句柄的线程上执行指定的委托。

它这样做的方式是将消息发布到拥有线程的消息队列中,并等待该线程处理该消息。

因此,Invoke是一个阻塞调用,在调用的委托被调用之前不会返回。

现在,您的代码阻塞的可能原因是您的主 GUI 线程已经在等待其他事情发生,很可能您的外部程序已经完成。

因此,它实际上并没有处理消息。

如果是这个原因,那么这里的解决方案就是去掉GUI线程的阻塞部分。不要坐等外部程序完成,而是派出一个等待它完成的任务,然后在它完成时在主窗体上引发适当的事件。同时,主线程可以自由处理消息、更新文本框等。

请注意,这意味着如果启动外部程序是为了响应事件(例如单击按钮),您可能需要在程序运行时禁用部分用户界面,以避免用户单击按钮两次,启动两个并行执行都将报告到同一个文本框。

结论:多线程编程很难!

于 2013-10-30T11:01:02.983 回答