c#中线程模式的正确回调

时间:2016-06-02 07:23:48

标签: c# multithreading events asynchronous

我正在使用线程从我的程序连接到多个客户端(PLC)。程序将发送数据并从PLC接收响应。问题是,当处于调试模式时,(切换断点)一次一步......程序工作非常好!,接收到的ID确认它来自其中一个线程..但如果我只是调试而不切换任何断点,响应事件将收到相同的ID,虽然在不同的线程上......可能是错误的......

Debugging mode with breakpoint:

Debugging mode without breakpoint:

以下是我的代码

开始申请:

private void StartRequest()
{            
    foreach (ModbusTCP work in works)
    {
        work.Connect();
        Thread.Sleep(1000);
        if (work.Connected)
        {
            try
            {
                Thread thread = new Thread(new ThreadStart(() => work.StartReadHoldingRegister())) {
                    Name = ((ReadHoldingRegisterParam)work.SetReadHoldingRegisterParam).id.ToString(),
                    IsBackground = true
                };
                work.OnResponseEvent += new EventHandler<ModbusTCP.ResponseEventArgs>(modbus_OnResponseEvent);
                work.OnExceptionEvent += new EventHandler<ModbusTCP.ExceptionEventArgs>(modbus_OnExceptionEvent);                                            
                thread.Start();
                threads.Add(thread);
            }
            catch (ThreadStateException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        else
            work.Disconnect();
    }            
}

回应事件

private void modbus_OnResponseEvent(object sender, ModbusTCP.ResponseEventArgs e)
{
    lock (lockingObject)
    {
        if (e.data.Length > 0)
        {
            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                AddRow(RcvDataGrid, new PLCPacket() {
                    PLCId = e.id.ToString(),
                    PLCIp = "Test",
                    PLCTime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss tt"),
                    PLCData = ""
                });
            }));
        }
    }
}

2 个答案:

答案 0 :(得分:4)

您的变量work在线程之间共享。一旦线程被执行,它将获取变量work具有的任何值。这取决于每个线程的处理速度。当您使用调试器单步执行代码时,您不会遇到这种情况。

如果您在匿名方法之前捕获该值,那么您应该没问题:

 try
 {
      // capture the current value of the loop variable
      ModbusTCP localWork = work;
      // so the anonymous method uses the reference in localWork
      // instead of whatever value work has, which can be anywhere
      // the future, worst case after your loop is finished, where
      // work would hold the last value of the loop, and then
      // start all threads with that value.
      Thread thread = new Thread(
           new ThreadStart(
               () => localWork.StartReadHoldingRegister())) 
               { 
                  Name = ((ReadHoldingRegisterParam) localWork.SetReadHoldingRegisterParam).id.ToString(), 
                  IsBackground = true };
               });
       localWork.OnResponseEvent += new EventHandler<ModbusTCP.ResponseEventArgs>(modbus_OnResponseEvent);
       localWork.OnExceptionEvent += new EventHandler<ModbusTCP.ExceptionEventArgs>(modbus_OnExceptionEvent);                                            

答案 1 :(得分:0)

旁边评论:

lock (lockingObject)
{
    if (e.data.Length > 0)
    {
        this.Dispatcher.BeginInvoke(new Action(() =>
        {

此代码不太可能正确。在这里,您将获得原始线程中的锁定,然后提交新操作async。锁定范围为当前方法,因此将在BeginInvoke调用返回后立即释放,而不是在操作本身期间释放。锁实际保护的唯一操作是e.data.Length检查,它在参数(非共享)状态下运行,因此不需要保护。

将锁置于操作中会更有意义,但操作总是在主线程上执行,因此实际上不太可能需要保护(因为基本上是单线程)。很难准确地猜测你想要实现的是什么,而不是看到整个代码,但是lock(lockingObject)不太可能是必要的或有用的。