多线程下的线程交互和同步等待

前一段时间一直在研究多个DUT在多天线下的同步执行情况,经过了几天的摸索,大致搞明白了。今天有时间所以来记录一下这个过程。

        开场白!我们先来讲一讲一个DUT和多根天线下的情况,这种情况是最简单的!因为切换一次天线,DUT就测量一个值,所以完全可以看做是乒乓式的交互。可以用两种方式进行实现,临界区和互斥体。

        1. 临界区就没太多好讲的了,可以定义一个全局变量,然后用临界区对该变量进行加锁访问。大致代码如下:最近在学C#,所以用C#演示

    class Program

    {

        private static int g_RunCount = 30;

        private static readonly object lockObj = new object();

        static bool bRunning = true;

        static void Main(string[] args)

        {

            Thread threadAnt = new Thread(AntSwitch);

            Thread threadDut = new Thread(DutExecute);

            threadAnt.Start();

            threadDut.Start();

            threadAnt.Join();

            threadDut.Join();

            Console.ReadKey();

        }

        private static void AntSwitch()

        {

            for (int j = 0; j < g_RunCount; j++)

            {

                lock (lockObj)

                {

                    if (bRunning)

                    {

                        Console.WriteLine(" -> AntSwitch[{0}]", j);

                        bRunning = false;

                    }

                    else

                    {

                        j--;

                    }

                }

            }

        }

        private static void DutExecute()

        {

            for (int i = 0; i < g_RunCount; i++)

            {

                lock (lockObj)

                {

                    if (!bRunning)

                    {

                        Console.WriteLine(" <- DutExecute[{0}]", i);

                        bRunning = true;

                    }

                    else

                    {

                        i--;

                    }

                }

            }

        }

    }

        2. 互斥体Mutex也很简单,和临界区没啥大的区别,就是把锁换成Mutex就搞定。注意初始化的时候:如果是在一个线程内进行互斥体则把第一个变量设置为true。如果是在另外其他的线程中使用互斥体,则第一个变量设置为false。具体的代码如下所示:仅供参考

    class Program

    {

        private static int g_RunCount = 30;

        private static Mutex mutex = new Mutex(false, "MainMutex");

        static bool bRunning = true;

        static void Main(string[] args)

        {

            Thread threadAnt = new Thread(AntSwitch);

            Thread threadDut = new Thread(DutExecute);

            threadAnt.Start();

            threadDut.Start();

            threadAnt.Join();

            threadDut.Join();

            Console.ReadKey();

        }

        

        private static void AntSwitch()

        {

            for (int j = 0; j < g_RunCount; j++)

            {

                mutex.WaitOne();

                {

                    if (bRunning)

                    {

                        Console.WriteLine(" -> AntSwitch[{0}]", j);

                        bRunning = false;

                    }

                    else

                    {

                        j--;

                    }

                }

                mutex.ReleaseMutex();

            }

        }

        private static void DutExecute()

        {

            for (int i = 0; i < g_RunCount; i++)

            {

                mutex.WaitOne();

                {

                    if (!bRunning)

                    {

                        Console.WriteLine(" <- DutExecute[{0}]", i);

                        bRunning = true;

                    }

                    else

                    {

                        i--;

                    }

                }

                mutex.ReleaseMutex();

            }

        }

    }

这里总结一下互斥体和临界区的区别:都可以实现访问,但是因为互斥体是内核态的东西,所以性能上略差,像多天线这种测试程序对测试时间要求非常高,所以不建议使用互斥体

    3. 重头戏来了,我们来讲讲多DUT和多天线的测试:

        首先来看看我们的通信图示:

 

        要求做到的目标:为了提升测试速率,每切换一根天线,则4个DUT同时去测试某个Band的相关指标;然后再切、再测。一直遍历所有天线。

        这样就把整个测试资源分为2组,一组是是多根天线,每根天线之间是串行执行;另外一组是4个DUT,他们之间是并行执行;这2组之间又需要交互执行。整个过程是不是有点绕?你觉得上面单靠临界区或互斥体能够解决这种状态吗?

        其实可以简单的梳理一下:【以上舞台表演为例】

        a. 两个分组之间只要能够保障他们依次执行即可【也可以理解为乒乓执行】,即天线切一次,所有手机测一次。

        b. 天线直接按照编号顺序切换就行,每切换一次天线之后就通知DUT说:我天线已经上台预报节目了,我现在通知你们DUT,该你们上台表演了。在你们表演完之后再通知我切换到下一个频道【此时我应该一直保持原状等待】,你们再上台表演。

        c. DUT要等待天线的通告消息,一旦有信号就立刻上台表演。但是表演是一群DUT,这群DUT上台的顺序不确定,上台后表演的节奏快慢不确定,但是唯一能够确定的就是要:保障这群DUT每次都能同时上台和同时下台。

        有没有绕晕?

        这样一分析基本就可以确定了:天线和DUT之间应该使用事件方式进行信息的传递,因为要实现通知功能,所以使用手动事件实现。除此之外,DUT之间还要使用事件保障每个DUT的步调一致【同时上台和同时下台】。那实现方式就来了:

    class Program

    {

        const int nStation = 4;         // DUT数量

        const int g_RunCount = 30;      // 天线数量

        public static ManualResetEvent[] g_stationTrigEvents = new ManualResetEvent[nStation];    // 用射频开关锁槽位

        public static ManualResetEvent[] g_antTriggerEvents = new ManualResetEvent[nStation];       // 用槽位锁射频开关

        public static ManualResetEvent[] g_allRunEvts = new ManualResetEvent[nStation];              // 每个Station间互锁

        static void Main(string[] args)

        {

            Thread[] thread = new Thread[nStation];

            for (int i = 0; i < nStation; i++)

            {

                g_stationTrigEvents[i] = new ManualResetEvent(false);

                g_antTriggerEvents[i] = new ManualResetEvent(false);

                g_allRunEvts[i] = new ManualResetEvent(false);

                //g_allRunSecEvts[i] = new ManualResetEvent(false);

            }

            Thread threadAnt = new Thread(AntSwitch);

            threadAnt.Start();

            for (int i = 0; i < nStation; i++)

            {

                thread[i] = new Thread(Thread_phoneInit);

                thread[i].Name = string.Format("      Begin_{0}        ",  i);

                Object[] dutReq = new Object[] { i, thread[i].Name };

                thread[i].Start(dutReq);

            }

            threadAnt.Join();

            for (int i = 0; i < nStation; i++)

            {

                thread[i].Join();

            }

            Console.ReadKey();

        }

        

        private static void AntSwitch()

        {

            for (int j = 0; j < g_RunCount; j++)

            {

                Console.WriteLine("AntSwitch[{0}]", j);

                SetStartSignal();

            }

        }

        public static bool SetStartSignal()

        {

            for (int count = 0; count < g_stationTrigEvents.Length; count++)

            {

                if (null == g_stationTrigEvents[count])

                {

                    continue;

                }

                g_stationTrigEvents[count].Set();

            }

            bool bok = WaitHandle.WaitAll(g_antTriggerEvents/*, 1100*/);

            if (!bok)

            {

                Console.WriteLine("SetStartSignal WaitAll fail.");

            }

            else

            {

                Console.WriteLine("SetStartSignal WaitAll ok.");

            }

            for (int count = 0; count < g_antTriggerEvents.Length; count++)

            {

                g_antTriggerEvents[count].Reset();

                g_allRunEvts[count].Reset();

            }

            return bok;

        }

        private static void Thread_phoneInit(object obj)

        {

            Object[] req = obj as Object[];

            int iSta = (int)req[0];

            for (int j = 0; j < g_RunCount; j++)

            {

                bool bok = true;

                bok &= WaitHandle.WaitAll(g_stationTrigEvents/*, 1000*/);

                if (!bok)

                {

                    Console.WriteLine(iSta + " WaitStations g_stationTrigEvents fail.");

                }

                else

                {

                    Console.WriteLine(iSta + " WaitStations g_stationTrigEvents Ok.");

                }

                Console.WriteLine("{0} Thread[{1}] - Run times[{2}]", req[1], iSta, j);

                g_allRunEvts[iSta].Set();

                bok = WaitHandle.WaitAll(g_allRunEvts/*, 1000*/);

                if (!bok)

                {

                    Console.WriteLine(iSta + " Thread_phoneInit g_allRunEvts fail.");

                }

                else

                {

                    Console.WriteLine(iSta + " Thread_phoneInit g_allRunEvts ok.");

                }

                g_stationTrigEvents[iSta].Reset();

                g_antTriggerEvents[iSta].Set();

            }

        }      

    }

    直接用三种事件列表保障整个过程的执行,完美的解决这个问题。

    当然,应用到实际的场景会更加的复杂一些,因为要考虑到随时有DUT退出测试,天线切换失败怎么处理等等特殊事件考虑【我的真实代码里面的逻辑很复杂,不变演示给大家】,这里只记录基本的实现场景,供大家参考!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值