先加入 manuStrip
using System.Net; // 網路通訊協定
using System.Net.Sockets; // 網路插座功能
using System.Threading; // 多執行緒1. 設計一個Thread class
public class UDPThread
{ManualResetEvent m_shutdownEvent = new ManualResetEvent(false);
ManualResetEvent m_pauseEvent = new ManualResetEvent(true);
Thread m_thread;
int m_cnt = 0; public UDPThread() { }public void Job()
{while(true)
{m_pauseEvent.WaitOne(Timeout.Infinite);
if (m_shutdownEvent.WaitOne(0)) break;m_cnt++;
}
}
public void Start()
{ m_thread = new Thread(Job);m_thread.Start();
}
public void Pause()
{m_pauseEvent.Reset();
}
public void Resume()
{m_pauseEvent.Set();
}
public void Stop()
{m_shutdownEvent.Set();
m_pauseEvent.Set();
m_thread.Join();
}
}
2. 執行緒中如何更新UI?
2.1 加入Label設定/取值功能
public Label CntLabel{set;
get;
}
2.2 加入更新UpdateUI
public void UpdateUI()
{ if (CntLabel.InvokeRequired) { CntLabel.BeginInvoke( (Action)( () => { UpdateUI(); }) );}
else {CntLabel.Text = m_cnt.ToString();
}
}
2.3 UI加入對應事件
private void 開始ToolStripMenuItem_Click(object sender, EventArgs e)
{ udp1= new UDPThread();udp1.CntLabel = label1;
udp1.Start();
}
private void 暫停ToolStripMenuItem_Click(object sender, EventArgs e)
{udp1.Pause();
}
private void 繼續ToolStripMenuItem_Click(object sender, EventArgs e)
{udp1.Resume();
}
private void 停止ToolStripMenuItem_Click(object sender, EventArgs e)
{udp1.Stop();
}
private void 開始ToolStripMenuItem1_Click(object sender, EventArgs e)
{ udp2 = new UDPThread();udp2.CntLabel = label2;
udp2.Start();
}
private void 暫停ToolStripMenuItem1_Click(object sender, EventArgs e)
{udp2.Pause();
}
private void 繼續ToolStripMenuItem1_Click(object sender, EventArgs e)
{udp2.Resume();
}
private void 停止ToolStripMenuItem1_Click(object sender, EventArgs e)
{udp2.Stop();
}
啟動執行緒1和執行緒2後, 畫面如下:
2.4 在Form Closing加入防呆
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{if (udp1 != null) udp1.Stop();
if (udp2 != null) udp2.Stop();
}
=================================================================
3. UI加入IP和PORT設定,
3.1 新增IP和Port的存取
修改開始執行緒1
同時, 修改開始執行緒2
private void 開始ToolStripMenuItem1_Click(object sender, EventArgs e)
{ udp2 = new UDPThread();udp2.IP = textBox1.Text;
udp2.Port = Convert.ToInt16(numericUpDown2.Value);
udp2.CntLabel = label2;
udp2.Start();
}
準備加入監聽InitUDP()和接收資料RecvData()功能
3.2 UDP初始化
private void InitUDP()
{if (m_udp == null)
if (JobType == JOBTYPE.RECVDTA)m_udp = new UdpClient(Port); // 創建一個UDP實體
else m_udp = new UdpClient();if (m_IPEP == null) m_IPEP = new IPEndPoint(IPAddress.Parse(IP), Port); // 創建一個連線插座
}
3.3 接收資料RecvData()
public void RecvData()
{ try {if (m_udp.Available > 0) // 很重要, 否則會被咬住
{ if(CntLabel.InvokeRequired) { CntLabel.BeginInvoke( (Action)(() => { RecvData(); }) ); }else{byte[] B = m_udp.Receive(ref m_IPEP);
CntLabel.Text = Encoding.Default.GetString(B);
}
}
}
catch (Exception e) { //MessageBox.Show(e.Message);}
}
3.4 傳送資料SendData()
public void SendData()
{ try { if (CntLabel.InvokeRequired) { CntLabel.BeginInvoke((Action)(() => { SendData(); }));}
else { string str = m_cnt.ToString(); byte[] B = Encoding.Default.GetBytes(str);m_udp.Send(B, B.Length, IP, Port);
CntLabel.Text = str;
}
}
catch (Exception e) { //MessageBox.Show(e.Message);}
}
3.5 判定該執行緒執行SendData()或是RecvData()
public enum JOBTYPE { SENDDATA, RECVDTA }; // 傳送, 接收
3.6 修改Job內容
public void Job()
{InitUDP();
while(true)
{m_pauseEvent.WaitOne(Timeout.Infinite);
if (m_shutdownEvent.WaitOne(0)) break;m_cnt++;
if (JobType == JOBTYPE.RECVDTA) {RecvData();
//UpdateUI();}
else {SendData();
}
Thread.Sleep(m_timeSleep);
}
}
3.7 修改開始執行緒1
private void 開始ToolStripMenuItem_Click(object sender, EventArgs e)
{ udp1= new UDPThread();udp1.IP = textBox1.Text;
udp1.Port = Convert.ToInt16(numericUpDown1.Value);
udp1.CntLabel = label1;
udp1.JobType = JOBTYPE.RECVDTA;
udp1.Start();
}
3.7 修改開始執行緒2
private void 開始ToolStripMenuItem1_Click(object sender, EventArgs e)
{ udp2 = new UDPThread();udp2.IP = textBox1.Text;
udp2.Port = Convert.ToInt16(numericUpDown1.Value);
udp2.CntLabel = label2;
udp2.JobType = JOBTYPE.SENDDATA;
udp2.Start();
}
=================================================================
3.8 測試結果:
執行緒1: 負責接收
執行緒2: 負責傳送
執行緒1如果臨時暫停, 執行緒2送來的資料還是會保留在bufffer區,
等執行緒1繼續執行, 資料會陸續取出
=================================================================
參考資料
1. C# 如何創建, 暫停, 繼續, 終止一個執行緒(Thread)
2. Visual C# 2012 網路程式設計 線上遊戲實作








留言列表
