最近試著用C#練習寫BT(bluetooth)搜尋程式,為了讓搜尋時視窗不要因為搜尋時而導致視窗無法移動,因此,最簡單的方式,就是在增加一個thread來處理搜尋的動作,等處理完後再將結果顯示在ListBox上:
##CONTINUE##
public void Search_Click(object sender, EventArgs e)
{
this.listBox_devices.Items.Clear();
this.Text = "Search Bluetooth Device...";
this.search_thread = new Thread(new ThreadStart(search));
search_thread.Start();
}
private void search()
{
this.listBox_devices.Items.Clear();
InTheHand.Net.Sockets.BluetoothClient bc = new InTheHand.Net.Sockets.BluetoothClient();
InTheHand.Net.Sockets.BluetoothDeviceInfo[] array = bc.DiscoverDevices();
for (int i = 0; i <>
this.address_array[i] = array[i].DeviceAddress;
this.listBox_devices.Items.Add(array[i].DeviceName);}
}
上面程式在按下Search按鈕後將會啟動Search thread處理搜尋的功能,搜尋完後會將蒐尋到的BT Devices的名稱儲存下來並顯示在ListBox中,看起來似乎沒甚麼問題,但程式在按下Search按鈕後程式就當掉了!
原來問題在於,只有建立Windows Form的thread才能存取、修改Form或控制項的內容,因為.NET的控制項在設計時是執行緒相依(thread affnity),只有在建立該Form或控制項的執行緒才能安全地存取其屬性。
當控制項的狀態改變時,系統會把它轉換成視窗訊息,放到訊息佇列中(Message Queue),再由程式處理!
不過,將訊息放入訊息佇列(Message Queue)的方式分為兩種,一種為SendMessage(),一個則為PostMessage()。前者為呼叫者會停住直到訊息被處理為止;而後者呼叫端則是將訊息放置訊息佇列後即立刻返回。
而在.NET裡,所有Form或是控制項的屬性變更,皆由SendMessage()送出訊息!
因此,在上面的程式中,當主執行緒等待搜尋執行緒完成,而搜尋執行緒因為嚐試更新控制項,因此呼叫了SendMessage(),而SendMessage()會因此使搜尋執行緒停住,因為它必須停主執行緒將SendMessage()送出的訊息處理,而主執行緒也在等待搜尋執行緒執行完畢,因此變成互相等待的情形,程式因此當掉了!