一、多线程是什么?为什么要用多线程?
线程:进程中的一个独立控制单元,线程在控制着进程的执行。一个进程中至少有一个进程,可以有多个线程。
为什么要用多线程:
1、为更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待;
2、进程之间不能共享数据,线程可以;
3、系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;
二、线程的生命周期:
1、新建 :从新建一个线程对象到程序start() 这个线程之间的状态,都是新建状态;
2、就绪 :线程对象调用start()方法后,就处于就绪状态,等到线程调度器的调度;
3、运行 :就绪状态下的线程在获取CPU资源后就可以执行run(),此时的线程便处于运行状态,运行状态的线程可变为就绪、阻塞及死亡三种状态。
等待/阻塞/睡眠 :在一个线程执行了sleep(睡眠)、suspend(挂起)等方法后会失去所占有的资源,从而进入阻塞状态,在睡眠结束后可重新进入就绪状态。
4、终止 :run()方法完成后或发生其他终止条件时就会切换到终止状态。
三、3个线程间2队列数据交互与通讯举例
数据:
3.1.1、网络数据队列
private static Queue NetDataList1 = new Queue();
3.1.2、页面数据队列
private static Queue NetDataList2=new Queue();
线程:
3.2.1、UDP单播接收数据线程AddDataToList
3.2.2、网络处理线程NetDataProcThread
3.2.3、页面处理线程NetDataToFormProc
AddDataToList线程负责将接收到网络数据写入队列NetDataList1 ,NetDataProcThread负责从队列NetDataList1读出关键数据并转换为页面可识别的packData数据写入到队列NetDataList2中,而NetDataToFormProc是依据从NetDataList2读出的控件数据,自动更新页面展示。
3.3代码各线程实现逻辑如下:
3.3.1、单播创建
private static UdpClient CreateUnicastUDP(int sourceIndex)
{
try
{
UdpClient udp = new UdpClient(0);
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
udp.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
udp.BeginReceive(UdpRecvFunc, sourceIndex);
return udp;
}
catch (System.Exception e)
{
throw new Exception("单播创建失败. ");
}
}
3.3.2、回调函数实现方法
private static void UdpRecvFunc(IAsyncResult Result)
{
int sourceIndex = (int)Result.AsyncState;
if (UnicastClientDic.ContainsKey(sourceIndex))
{
UdpClient udp = UnicastClientDic[sourceIndex];
try
{
System.Threading.Interlocked.Exchange(ref SubDevice.LastAliveTime, Environment.TickCount);
IPEndPoint remote = null;
byte[] Buffer = udp.EndReceive(Result, ref remote);
udp.BeginReceive(UdpRecvFunc, sourceIndex);
Protocal.AddDataToList1(remote, ref Buffer, sourceIndex);
}
catch (Exception ex)
{
udp.Close();
UnicastClientDic.Remove(sourceIndex);
}
}
}
3.3.3、NetData数据写入队列实现
public static void AddDataToList1(IPEndPoint ipinf, ref byte[] data, int sourceIndex)
{
NetData nd = new NetData(ipinf, ref data, sourceIndex);
lock(NetDataList1)
{
NetDataList1.Enqueue(nd);
}
lock (locker1)
{
Monitor.Pulse(locker1);
}
}
3.3.4网络线程接收并转换为页面可识别数据包
private static void NetDataProcThread()
{
NetData stNetData = new NetData();
while (!ThreadEnd)
{
lock (locker1)
{
Monitor.Wait(locker1);
}
lock (NetDataList1)
{
while (NetDataList1.Count > 0)
{
stNetData = NetDataList1.Dequeue();
NetDataParse(ref stNetData);
}
}
}
}
private static void NetDataParse(ref NetData stNetData)
{
tagNetMsg stNetMsg = (tagNetMsg)DataPack.ToStruct(stNetData.Data, typeof(tagNetMsg));
IPAddress ip = stNetData.IP.Address;
PackData CoreData = new PackData(ref stNetMsg);
CoreData.IP = ip;
CoreData.NetMsg = stNetMsg;
CoreData.msg_tag = (short)stNetMsg.msg_tag;
CoreData.SourceIndex = stNetData.SourceIndex;
AddDataToList2(CoreData);
}
public static void AddDataToList2(PackData Data)
{
lock (NetDataList2)
{
NetDataList2.Enqueue(Data);
}
lock (locker2)
{
Monitor.Pulse(locker2);
}
}
四、单播网络线程创建和关闭通过UdpStart和UdpStop实现。
五、NetDataProcThread开启与关闭实现方法
public static void ProtocalInit()
{
ProcThread.Name = “ProcThread”;
ProcThread.Start();
}
public static bool ProtocalEnd()
{
ThreadEnd = true;
lock (locker1)
{
Monitor.Pulse(locker1);
}
ProcThread.Join();
return true;
}
六、页面线程为主线程同时可后台启动与停止后台线程
public static bool Start()
{
if (false == UDP.UdpInit(LocalIP))
{
return false;
}
if (false == Protocal.ProtocalInit( InterFaceList, locker))
{
return false;
}
return true;
}
public static bool Stop()
{
if (false == UDP.UdpEnd())
{
return false;
}
if (false == Protocal.ProtocalEnd())
{
return false;
}
return true;
}