qq_41285454 2024-11-27 10:20 采纳率: 0%
浏览 193
已结题

C#多线程假死或卡死问题


     for (int i = 0; i < axiss.Length; i++)
     {
         int axis = axiss[i];
         LogHelper.Write($"1--执行的轴号是{axis}");
         var dyminW = new DummyWork2();
         dyminW.ZMotionDammyReady += new DummyWork2.ZAxisDyminWorkingEndEvent(OnDyminWorkingEnd);
         dyminW.zAxisDyminDropSenderEnd += new DummyWork2.ZAxisDyminDropSenderEndEvent(DropSender);
         dyminW.Start(axis, 0);
         LogHelper.Write($"2--执行的轴号是{axis}");
         dummyWork2s.Add(dyminW);
     }

通过上面的方式去调用下面的代码,下面的代码里面会经常使用Task.Run()异步线程进行硬件操作,最多一次控制6个轴(也就是new 6个DummyWork2),轴操作分为回吸运动和滴下运动,没管大概10000000的量,每次滴下大概1-20000,现在需要6个轴一起连续滴多少次(客户设置一般800-2000次)。现在遇到一个问题,就是我运动过程中会有某个轴突然停止运动,似乎是线程假死,有时候哪个轴就直接停止运动,有时候突然过段时间又开始运动,有的时候开启下次任务时,突然又接着上次的任务次数接着运动。哪位大佬帮忙看看,怎么解决?
 public class DummyWork2
 {
     public delegate void ZAxisDyminWorkingEndEvent(int axis, bool success);
     /// <summary>
     /// 轴工作已完成触发
     /// </summary>
     public ZAxisDyminWorkingEndEvent ZMotionDammyReady;

     public delegate void ZAxisDyminDropSenderEndEvent(int axis, int dropNum, int num);

     public ZAxisDyminDropSenderEndEvent zAxisDyminDropSenderEnd;
     public int Suckvalue;
     public int SuckSpeed;
     public int DropSpeed;
     public int DropPulse;
     public int DropNumber;
     public int DyminNum;
     public int DummyStartNum;
     public int Axis = -1;
     public int Num = 1;
     public int IntervalTime = Commond.Commond.ReadJson.typeNumber.IntervalTime;
///获取每个轴的回吸量
     public int SuckUnm()
     {
         try
         {
             LogHelper.Write($"{Axis}轴SuckUnm start!");
             int position = 0;
             var ans = Commond.Commond.Mc210Contorl.SscGetPosition(1, 1, Axis + 1, out position);
             if (ans != "success")
             {
                 Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_OPERATION);
                 Thread.Sleep(50);
                 Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SYSTEM);
                 Thread.Sleep(50);
                 Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SERVO);
                 Thread.Sleep(50);
                 SuckUnm();
             }
             var number = Convert.ToInt32(Commond.Commond.ReadJson.auto[Axis].SuckValue) - Convert.ToInt32(position);
             LogHelper.Write($"{Axis}轴SuckUnm end!");
             return number;
         }
         catch (Exception ex) { return 0; }
     }


     System.Threading.Timer _SuckState;
///回吸动作
     public void Suck(int dummyStartNum)
     {
         try
         {
             CancellationTokenSource cts = new CancellationTokenSource();
             Task.Run(() =>
             {
                 try
                 {
                     LogHelper.Write($"{Axis}轴回吸开始!");
                     DummyStartNum = dummyStartNum;
                     Suckvalue = SuckUnm();
                     LogHelper.Write($"{Axis}轴回吸量:{Suckvalue}!");
                     ZMotionTools.SetOp(Commond.Commond.IOConfig.ValveReverse[Axis], 0);
                     ZMotionTools.SetOp(Commond.Commond.IOConfig.ValveCorotation[Axis], 1);

                     if (Commond.Commond.Mc210WorkingStop)
                     {
                         LogHelper.Write($"{Axis}轴停止任务!");
                         _SuckState.Dispose();
                         cts.Cancel();
                         cts.Dispose();
                         AxisState = 1;
                         return;
                     }
                     var ret = Commond.Commond.Mc210Contorl.SscIncStart(1, 1, Axis + 1, Suckvalue, SuckSpeed, 1, 3);
                     if (ret != "success")
                     {
                         LogHelper.Write($"{Axis}轴回吸失败!");
                         Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_OPERATION);
                         Thread.Sleep(50);
                         Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SYSTEM);
                         Thread.Sleep(50);
                         Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SERVO);
                         Thread.Sleep(50);
                         if (Commond.Commond.Mc210WorkingStop)
                         {
                             LogHelper.Write($"{Axis}轴停止任务!");
                             _SuckState.Dispose();
                             cts.Cancel();
                             cts.Dispose();
                             AxisState = 1;
                             return;
                         }
                         ret = Commond.Commond.Mc210Contorl.SscIncStart(1, 1, Axis + 1, Suckvalue, SuckSpeed, 1, 3);
                         if (ret != "success")
                         {
                             cts.Cancel();
                             cts.Dispose();
                             AxisState = 1;
                             WarnTools.Warn($"{Axis + 1}轴回吸失败", WarnTypeENum.P轴报警, true, 1);
                             LogHelper.Write($"{Axis + 1}轴回吸失败");                              
                             return;
                         }
                     }
                     int Timeout = Convert.ToInt32(Suckvalue / (SuckSpeed));
                     int count = 0;
                     int state;
                     _SuckState = new Timer((s) =>
                     {
                         if (Commond.Commond.Mc210WorkingStop)
                         {
                             LogHelper.Write($"{Axis}轴停止任务!");
                             _SuckState.Dispose();
                             cts.Cancel();
                             cts.Dispose();
                             AxisState = 1;                              
                             return;
                         }
                         var status = Commond.Commond.Mc210Contorl.SscGetDriveFinStatus(1, 1, Axis + 1, SscApi.SSC_FIN_TYPE_SMZ, out state);
                         if (status == "success")
                         {
                             if (state == 1)
                             {
                                 _SuckState.Dispose();
                                 LogHelper.Write($"{Axis}轴回吸完成,开始第{dummyStartNum}任务!");
                                 cts.Cancel();
                                 cts.Dispose();
                                 Dummy();
                             }
                         }
                         else
                         {
                             Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_OPERATION);
                             Thread.Sleep(50);
                             Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SYSTEM);
                             Thread.Sleep(50);
                             Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SERVO);
                             Thread.Sleep(50);
                         }

                         if (count > 20)
                         {
                             _SuckState.Dispose();
                             cts.Cancel();
                             cts.Dispose();
                             AxisState = 1;
                             WarnTools.Warn($"{Axis + 1}轴吐料模式回吸超时!", WarnTypeENum.P轴报警, true, 1);
                             LogHelper.Write($"{Axis + 1}轴吐料模式回吸超时");                              
                             return;
                         }
                         count++;
                     }, null, Timeout, 500);
                 }
                 catch (Exception ex)
                 {
                     cts.Cancel();
                     cts.Dispose();
                     AxisState = 1;
                     WarnTools.Warn($"{Axis + 1}轴吐料模式回吸错误:{ex.Message}!", WarnTypeENum.P轴报警, true, 1);
                     LogHelper.Write($"{Axis + 1}轴吐料模式回吸错误:{ex.Message}");
                 }
             }, cts.Token);
         }
         catch (Exception ex)
         {
             LogHelper.Write($"{Axis + 1}轴吐料模式回吸错误:{ex.Message}");
             AxisState = 1;
         }
     }

    ///滴下动作,当剩余的量不够滴下动作时回吸
     public void Dummy()
     {
         CancellationTokenSource cts = new CancellationTokenSource();
         LogHelper.Write($"Dummy Start1->{Axis} DummyStartNum {DummyStartNum} DyminNum {DyminNum}");
         var dummy = Task.Run(() =>
         {
             try
             {
                 LogHelper.Write($"Dummy Start2->{Axis}");
                 for (int i = DummyStartNum; i < DyminNum; i++)
                 {
                     ZMotionTools.SetOp(Commond.Commond.IOConfig.ValveReverse[Axis], 0);
                     ZMotionTools.SetOp(Commond.Commond.IOConfig.ValveCorotation[Axis], 0);
                     DropSenser.Zero(DropSenserNum(Axis));
                     for (int j = 0; j < DropNumber; j++)
                     {
                         if (Commond.Commond.Mc210WorkingStop)
                         {
                             cts.Cancel();
                             cts.Dispose();
                             AxisState = 1;
                             LogHelper.Write($"Dummy stop->{Axis}");                             
                             return;
                         }

                         var ret = Commond.Commond.Mc210Contorl.SscIncStart(1, 1, Axis + 1, -DropPulse, DropSpeed, 1, 5);
                         if (ret == "success")
                         {
                             Thread.Sleep(Convert.ToInt32(Commond.Commond.ReadJson.auto[Axis].DropInterval) + Convert.ToInt32(DropPulse / (DropSpeed)));
                         }
                         else
                         {
                             Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_OPERATION);
                             Thread.Sleep(10);
                             Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SYSTEM);
                             Thread.Sleep(10);
                             Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SERVO);
                             Thread.Sleep(10);
                             Commond.Commond.Mc210Contorl.SscIncStart(1, 1, Axis + 1, -DropPulse, DropSpeed, 1, 5);
                         }
                     }
                     zAxisDyminDropSenderEnd?.Invoke(Axis, DropSenser.GetCount(DropSenserNum(Axis)), i + 1);

                     int position = 0;
                     var ans = Commond.Commond.Mc210Contorl.SscGetPosition(1, 1, Axis + 1, out position);
                     if (ans != "success")
                     {
                         Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_OPERATION);
                         Thread.Sleep(50);
                         Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SYSTEM);
                         Thread.Sleep(50);
                         Commond.Commond.Mc210Contorl.SscResetAlarm(1, 1, Axis + 1, SscApi.SSC_ALARM_SERVO);
                         Thread.Sleep(50);
                         Commond.Commond.Mc210Contorl.SscGetPosition(1, 1, Axis + 1, out position);
                     }
                     if (position > DropPulse * DropNumber)
                     {
                         Thread.Sleep(IntervalTime);
                     }
                     else
                     {
                         cts.Cancel();
                         cts.Dispose();
                         Suck(i + 1);                         
                         return;
                     }
                 }
                 AxisState = 1;
                 return;
             }
             catch (Exception ex)
             {
                 cts.Cancel();
                 cts.Dispose();
                 WarnTools.Warn($"{Axis}轴Dummy运动错误:{ex.Message}", WarnTypeENum.P轴报警, true, 1);
                 LogHelper.Write($"{Axis}轴Dummy运动错误:{ex.Message}");
                 AxisState = 1;               
             }
         }, cts.Token);


     }

     public int DropSenserNum(int input)
     {
         try
         {
             int num = 0;
             switch (input)
             {
                 case 0:
                     num = Commond.Commond.IOConfig.InputAxis0DorpSenser;
                     break;
                 case 1:
                     num = Commond.Commond.IOConfig.InputAxis1DorpSenser;
                     break;
                 case 2:
                     num = Commond.Commond.IOConfig.InputAxis2DorpSenser;
                     break;
                 case 3:
                     num = Commond.Commond.IOConfig.InputAxis3DorpSenser;
                     break;
                 case 4:
                     num = Commond.Commond.IOConfig.InputAxis4DorpSenser;
                     break;
                 case 5:
                     num = Commond.Commond.IOConfig.InputAxis5DorpSenser;
                     break;
                 default:
                     num = 0;
                     break;
             }
             return num;
         }
         catch (Exception ex)
         {
             return -1;
         }
     }

     public void Start(int axis, int type)
     {
         CancellationTokenSource cts = new CancellationTokenSource();
         var a = Task.Run(() =>
         {
             try
             {
                 //工作流程
                 //状态监听
                 Axis = axis;
                 SuckSpeed = Commond.Commond.UCMppConditions.MPPSuckSpeed[axis];
                 DropSpeed = Commond.Commond.UCMppConditions.MPPDropSpeed[axis];
                 DropPulse = Convert.ToInt32(Commond.Commond.ReadJson.auto[axis].StandardPulse);
                 DropNumber = Convert.ToInt16(Commond.Commond.ReadJson.auto[axis].DropCount);
                 if (type == 0)
                     Num = Commond.Commond.ReadJson.typeNumber.DyminNum;
                 else
                     Num = Commond.Commond.ReadJson.typeNumber.Dymin2Num;
                 Suckvalue = Convert.ToInt32(Commond.Commond.ReadJson.auto[Axis].SuckValue);
                 DyminNum = Convert.ToInt32(Suckvalue / (DropPulse * DropNumber)) * Num;
                 LogHelper.Write($"Start的轴号是{Axis}");
                 if (Commond.Commond.Mc210WorkingStop)
                 {
                     LogHelper.Write($"{Axis}轴停止任务!");
                     _SuckState.Dispose();
                     cts.Cancel();
                     cts.Dispose();
                     AxisState = 1;
                     return;
                 }
                 cts.Cancel();
                 cts.Dispose();
                 Suck(0);
             }
             catch (Exception ex)
             {
                 cts.Cancel();
                 cts.Dispose();
                 WarnTools.Warn($"{axis + 1}轴运动报错:{ex.Message}", WarnTypeENum.P轴报警, true, 1);
                 AxisState = 1;
             }
         }, cts.Token);
     }
     public int axisState;
     public int AxisState
     {
         get => axisState;
         set
         {
             axisState = value;
             if (axisState == 1)
             {
                 LogHelper.Write($"{Axis}轴完成");
                 ZMotionDammyReady?.Invoke(Axis, true);
             }
         }
     }
 }
  • 写回答

40条回答 默认 最新

  • M_try的小尾巴 2024-11-27 10:54
    关注
    获得0.15元问题酬金

    提示: 本回答参考 AIGC,由本人审核整理提供。若问题解决,望采纳;如仍有疑问,请评论回复。

    问题分析

    从代码和描述来看,问题主要集中在多线程操作中,特别是使用 Task.Run() 进行硬件操作时,某些轴可能会突然停止运动,表现为线程假死或卡死。这种情况可能是由于以下几个原因导致的:

    1. 资源竞争:多个线程同时访问和修改共享资源,导致资源竞争和死锁。
    2. 硬件操作阻塞:硬件操作可能因为某些原因(如硬件故障、通信问题等)导致线程阻塞。
    3. 任务取消和异常处理:任务取消和异常处理不当可能导致线程无法正常结束。
    4. 线程同步问题:线程间的同步机制可能存在问题,导致某些线程无法正常执行。

    解决方案

    1. 资源竞争问题

    确保所有共享资源(如硬件控制接口、状态变量等)在多线程环境下是线程安全的。可以使用 lock 关键字或其他同步机制来保护共享资源。

    private readonly object _lock = new object();
    
    public void Suck(int dummyStartNum)
    {
        lock (_lock)
        {
            // 保护共享资源的操作
        }
    }
    

    2. 硬件操作阻塞问题

    在硬件操作中加入超时机制,避免因硬件问题导致线程长时间阻塞。可以使用 Task.WaitAsyncCancellationToken 来实现超时控制。

    public async Task SuckAsync(int dummyStartNum, CancellationToken cancellationToken)
    {
        try
        {
            var result = await Commond.Commond.Mc210Contorl.SscIncStartAsync(1, 1, Axis + 1, Suckvalue, SuckSpeed, 1, 3, cancellationToken);
            if (result != "success")
            {
                // 处理错误
            }
        }
        catch (OperationCanceledException)
        {
            // 处理超时
        }
    }
    

    3. 任务取消和异常处理

    确保在任务取消时,所有资源都被正确释放,并且异常被正确捕获和处理。

    public void Suck(int dummyStartNum)
    {
        using (var cts = new CancellationTokenSource())
        {
            try
            {
                Task.Run(() =>
                {
                    try
                    {
                        // 任务逻辑
                    }
                    catch (Exception ex)
                    {
                        // 异常处理
                    }
                }, cts.Token);
            }
            catch (Exception ex)
            {
                // 异常处理
            }
        }
    }
    

    4. 线程同步问题

    确保线程间的同步机制是正确的。可以使用 SemaphoreSlimManualResetEvent 等同步原语来控制线程的执行顺序。

    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
    
    public async Task SuckAsync(int dummyStartNum)
    {
        await _semaphore.WaitAsync();
        try
        {
            // 任务逻辑
        }
        finally
        {
            _semaphore.Release();
        }
    }
    

    总结

    通过以上方法,可以有效减少多线程操作中的假死或卡死问题。具体实施时,需要根据实际情况选择合适的同步机制和异常处理策略。同时,建议在开发过程中加入详细的日志记录,以便在问题发生时能够快速定位和解决问题。

    评论

报告相同问题?

问题事件

  • 系统已结题 12月5日
  • 创建了问题 11月27日