• 多线程通讯之C#设计笔记(十六)


    一、多线程是什么?为什么要用多线程?
    线程:进程中的一个独立控制单元,线程在控制着进程的执行。一个进程中至少有一个进程,可以有多个线程。
    为什么要用多线程:
     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;
    }

  • 相关阅读:
    办公环境保密管理制度
    咖啡店小程序:吸引顾客的创新营销手段
    写给Python社群的第8课:Python异常,你必须掌握的技术点
    with语句和上下文管理器
    主流的深度学习推理架构有哪些(NCNNN)
    基于Xml方法的Bean的配置-实例化Bean的方法-构造方法
    如何快速定位 dpdk memzone 内存泄露问题?
    Windows 找不到文件'(null)'.请确定文件名是否正确后,再试一下.要搜索文件,如何解决?
    Go 1.18 最强代码自测方法!
    iOS黑(灰)白化实现方案
  • 原文地址:https://blog.csdn.net/weixin_48408892/article/details/127937053