2012-07-23 61 views
1

我正在使用CANBus adapter,同時目前正在使用Microsoft Visual C#2010 Express編寫它的代碼。我試圖製作一個GUI,它運行一個單獨的線程,通過CANBus串行適配器從TEKTRONIX 020-2924-XX DPO DEMO 2板讀取消息。在CANBus適配器的隱藏代碼中終止線程

我試圖找出終止線程的安全方式(稱爲setReceiveCallBackThread)。但是,這個特殊的線程是特殊的,線程的代碼不可用。它是CANBus API的一部分。

我已經搜索了各地網絡(特別是堆棧溢出)如何安全地停止線程。我發現使用中止方法應該始終是最後的手段。

因此,如果我決定我不能在一個線程上使用中止,我將不得不在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 
      { 

      } 

     } 
    } 
} 
+0

我原來的答覆是不正確的,檢查編輯。 – Tergiver 2012-07-23 22:03:04

+0

是的,這種代碼適合很難處理的類別。任何一種可從.NET調用的工業總線接口實際上都是用本地代碼實現的。 COM是樣板。中止這樣的代碼是一個不可行的過程,只有託管代碼可以以一致的方式中止。底層的winapi調用是WaitForSingleObjectEx(),它需要一個bAlertable參數。每個人都使用非Ex版本或傳遞FALSE。假設這是可能的,你將不得不放棄。 – 2012-07-23 22:21:40

+0

如果確實阻塞了WaitFor並且無法停止,請將Thread.IsBackgroundThread屬性設置爲true,以便在應用程序退出時終止它。 – Tergiver 2012-07-23 23:24:28

回答

0

我的原始答案不正確。根據API指南

定義將接收所有傳入 消息的回調函數。這是一個阻塞呼叫,必須在單獨的 線程上調用。要註銷此回調函數,可以使用等於NULL的cbfn調用canplus_setReceiveCallback。

該文件沒有說當被阻止的呼叫將退出,所以我的猜測是,當你用NULL調用canplus_setReceiveCallback時它會退出。

我沒有辦法輕鬆進行測試,但您可以在原始調用返回時通過輸出文本來驗證這一點。

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

你Stop_Click方法可以停止線程:

EASYSYNC.canplus_setReceiveCallBack(can, null); 
+0

線程似乎不會停止,除非我身體上用計算機硬連接CANBU和Demoboard。我甚至用邏輯來確保代碼只在線程完成時纔會繼續。我不認爲我能做的其他事情真的很多。但我像往常一樣感謝你。你的回答非常有幫助! – EmbedThis 2012-07-24 19:49:46