在 CANBus 适配器的隐藏代码中终止线程

Posted

技术标签:

【中文标题】在 CANBus 适配器的隐藏代码中终止线程【英文标题】:Terminate Thread in hidden code for CANBus adapter 【发布时间】:2012-07-23 21:00:13 【问题描述】:

我正在使用 CANBus adapter,目前正在 Microsoft Visual C# 2010 Express 中为其编写代码。我正在尝试制作一个运行单独线程的 GUI,用于通过 CANBus 串行适配器从 TEKTRONIX 020-2924-XX DPO DEMO 2 板读取消息。

我试图找出终止线程的安全方法(称为 setReceiveCallBackThread)。但是,此特定线程的特殊之处在于该线程的代码不可用。它是 CANBus API 的一部分。

我在网上搜索了有关如何安全停止线程的信息(尤其是堆栈溢出)。我发现使用中止方法应该始终是最后的手段。

因此,如果我决定不能在线程上使用 abort,我将不得不在 canplus_setReceiveCallBack 子例程中使用异常处理。但是,问题是我无法访问 canplus_setReceiveCallBack;代码是隐藏的。请记住,这是一种独特的情况,因为我无法访问代码。与所有其他可以看到回调函数代码的情况不同,此子例程中的代码是看不到的。

下面的代码应该是您分析问题所需的一切,并希望能找到在线程上使用 Abort() 的替代方法。

// CANSnifferForm.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;

namespace WindowsFormsApplication1



    public partial class CANSnifferForm : Form
    

        // per the api document. "This is a blocking call and must be called on a separate thread."
        // the code previously after setCallback... was never being reached because the call is blocking.
        // this calls the setCallbackThread function in another thread so processing can continue.
        Thread setReceiveCallBackThread;
        bool stop;

        int can; // Return value of canplus_open

        uint idFilter; // Filter values entered by user
        ulong lenFilter; 
        bool stopThread; // Used for stopping thread         

        public CANSnifferForm()
        
            InitializeComponent();
             del = new EASYSYNC.CallbackDelegate(callback);
        


        private void callback(ref EASYSYNC.CANMsg msg)
        
            // Populate the dataGridView
            if(InvokeRequired)
               BeginInvoke(del, msg);
            else
               this.dataGridView1.Rows.Add(msg.id, msg.len, msg.data, msg.timestamp);
        

        private EASYSYNC.CallbackDelegate del;

        private void StartRestart_Click(object sender, EventArgs e)
        
            this.CANSnifferStatusBox.Clear();
            this.CANSnifferStatusBox.AppendText("CAN closed");
            this.ProcessStatusBox.Clear();
            this.ProcessStatusBox.AppendText("Stopped");
            EASYSYNC.CANMsg msg = new EASYSYNC.CANMsg();
            msg.id = 1;
            msg.timestamp = 2;
            msg.flags = 3;
            msg.len = 4;
            msg.data = 5;

            // Attempt to open CANBus adapter
            can = EASYSYNC.canplus_Open(IntPtr.Zero, "1000", IntPtr.Zero, IntPtr.Zero, 0);

            if (can < 0)
            
                // CANBus Adapter not opened
                this.ErrorBox.Clear();
                this.ErrorBox.AppendText("Error opening CAN");
                return;
            

            // CANBus Adapter successfully opened
            this.CANSnifferStatusBox.Clear();
            this.CANSnifferStatusBox.AppendText("CAN open");

            // Initialize thread
            setReceiveCallBackThread = new Thread(() => EASYSYNC.canplus_setReceiveCallBack(can, del));

            // Attempt for CANBus adapter to listen
            if (EASYSYNC.canplus_Listen(can) < 0)
            
                // CANBus Adapter not listening
                this.ErrorBox.Clear();
                this.ErrorBox.AppendText("Error setting listen mode\n");
                EASYSYNC.canplus_Close(can);
                this.CANSnifferStatusBox.Clear();
                this.CANSnifferStatusBox.AppendText("CAN closed");
                return;
            

            // CANBus Adapter successfully listening
            this.CANSnifferStatusBox.Clear();
            this.CANSnifferStatusBox.AppendText("CAN Listening\n");

            // Place thread in background. Then start it
            setReceiveCallBackThread.IsBackground = true; 
            setReceiveCallBackThread.Start(); 
            while(!setReceiveCallBackThread.IsAlive);

            this.ProcessStatusBox.Clear();
            this.ProcessStatusBox.AppendText("Running\n");       


        

        private void FilterData_SelectedIndexChanged(object sender, EventArgs e)
        

        


        private void Form1_Load(object sender, EventArgs e)
        

        
        private void Stop_Click(object sender, EventArgs e)
        
           setReceiveCallBackThread.Abort(); // Stop thread
           while (setReceiveCallBackThread.IsAlive == true)
           

           

           this.ProcessStatusBox.Clear();
           this.ProcessStatusBox.AppendText("Stopped");


           // Attempt to flush CANBus Adapter
           if (EASYSYNC.canplus_Flush(can) < 0)
           
               // CANBus not flushing
               this.ErrorBox.Clear();
               this.ErrorBox.AppendText("Error flushing CAN");
               EASYSYNC.canplus_Close(can); // Close CANBus Adapter
               this.CANSnifferStatusBox.Clear();
               this.CANSnifferStatusBox.AppendText("CAN closed");
               return;
           
            // Attempt to reset CANBus Adapter
           if (EASYSYNC.canplus_Reset(can) < 0)
           
               // CANBus not resetting
               this.ErrorBox.Clear();
               this.ErrorBox.AppendText("Error resetting CAN");
               EASYSYNC.canplus_Close(can); // Close CANBus Adapter
               this.CANSnifferStatusBox.Clear();
               this.CANSnifferStatusBox.AppendText("CAN closed");
               return;
           

           this.CANSnifferStatusBox.Clear();
           this.CANSnifferStatusBox.AppendText("CAN closed");
           this.ErrorBox.Clear();
        

        private void FilterID_KeyDown(object sender, EventArgs e)
        

        

        private void FilterLength_KeyDown(object sender, EventArgs e)
        

        


        private void FilterID_MaskInputRejected(object sender, MaskInputRejectedEventArgs e)
        
            if (FilterIDBox.MaskFull)
            

            
            else if (e.Position == FilterIDBox.Mask.Length)
            

            
            else
            

            
        

        private void FilterLength_MaskInputRejected(object sender, MaskInputRejectedEventArgs e)
        
            if (FilterLengthBox.MaskFull)
            

            
            else if (e.Position == FilterLengthBox.Mask.Length)
            

            
            else
            

            

        
    

【问题讨论】:

我原来的答案不正确,请检查编辑。 是的,这种代码适合一个很难处理的类别。任何可从 .NET 调用的工业总线接口实际上都是在本机代码中实现的。 COM 是样板文件。中止此类代码是不行的,只有托管代码才能以一致的方式中止。底层的 winapi 调用是 WaitForSingleObjectEx(),它需要一个 bAlertable 参数。每个人要么使用非 Ex 版本,要么通过 FALSE。假设这是可能的,您将不得不放弃。 如果它确实在 WaitFor 上阻塞并且无法停止,请将 Thread.IsBackgroundThread 属性设置为 true,以便在应用程序退出时终止它。 【参考方案1】:

我原来的答案不正确。根据 API 指南

定义一个函数,它将在所有传入时接收回调 消息。这是一个阻塞调用,必须单独调用 线。要取消注册此回调函数, canplus_setReceiveCallback 可以调用 cbfn 等于 NULL。

文档没有说明被阻塞的调用何时退出,所以我猜测是当你调用canplus_setReceiveCallback时它会退出。

我没有办法轻松测试它,但是您可以通过在原始调用返回时输出文本来验证这一点。

setReceiveCallBackThread = new Thread(() =>  EASYSYNC.canplus_setReceiveCallBack(can, del); Trace.WriteLine("EASYSYNC thread terminated"); );

您的 Stop_Click 方法可以通过以下方式停止线程:

EASYSYNC.canplus_setReceiveCallBack(can, null);

【讨论】:

线程似乎不会停止,除非我在物理上硬断开 CANBU 和演示板与计算机的连接。我什至使用了逻辑来确保代码只有在线程完成时才会继续。我不认为我真的能做很多其他事情。但我像往常一样感谢你。你的回答很有帮助!!

以上是关于在 CANBus 适配器的隐藏代码中终止线程的主要内容,如果未能解决你的问题,请参考以下文章

React Native - 调试适配器进程已意外终止

从设置中隐藏同步适配器的虚拟帐户

worklight 多线程/servletcontext 调用适配器

vscode调试适配器已意外终止

C++ - 从客户端代码中隐藏模板参数

如何从客户端隐藏工作灯适配器过程,但对服务器可见?