首页 新闻 会员 周边

C# 多线程任务并行运行问题

0
悬赏园豆:50 [已解决问题] 解决于 2013-09-17 10:20

有四个任务 任务1:登陆验证 任务2:验证成功后从Web服务获取数据 任务3:验证成功后从数据库获取数据 任务4:使用2、3的数据执行一个方法 

在解决上述问题中,最早的方法是使用顺序执行1、2、3、4,现在对此做了优化,由于2、3两个任务之间是没有关联的,可以放到不同线程内执行,但是4的执行需要2、3同时完成作为条件,因此,在1执行后,创建两个全局标志位M2,M3,开启三个线程分别执行2、3、4,其中2、3执行完毕后会分别把M2、M3置为true,4的线程一直循环监听M2、M3,当两者都为true时开始执行4的任务。

现在想把这个方法做进一步优化,不希望4一直在询问2、3是否完成了,而是2、3完成了自己通知4。这里想到了使用异步委托BeginInvoke,创建一个4的回调函数,但难点是需要两个任务都执行完毕后才执行这个回调函数。

很自然地,我想到了再一次使用全局变量M2,M3。不过总觉得这种方式不太优雅?是否有更好的解决方案呢?

另外,想请教下这种类型的问题该归纳为那种问题,不太像并发问题吧?

林J的主页 林J | 菜鸟二级 | 园豆:202
提问于:2013-09-17 09:01
< >
分享
最佳答案
0

1 、Thread Join

2、Event Wait

收获园豆:50
Launcher | 高人七级 |园豆:45045 | 2013-09-17 09:28

感谢。

Thread.Join 是个好办法

Event Wait的话是需要创建两个全局的EventWaitHandle吧,效果没有ThreadJoin好。

两者的核心思想都是阻塞4任务的线程 等待2 3任务的线程执行完毕再执行,使我第一次优化的进阶版本。

不够我觉得我使用异步委托+回调函数也是个挺不错的想法。

林J | 园豆:202 (菜鸟二级) | 2013-09-17 10:09

@林J: 2,3是两个并行的任务,4 要等待 2,3同时完成后才能执行,那么你就需要一种机制来表示,你用全局变量 M2,M3的性能表现很差,因为你需要循环读取M2,M3的值来判断2,3是否都完成了,event 是一种更简洁、高效的通知机制。还有种方式,就是用 M2 和 spinwait 来实现,spinwait 比 event 更高效,但是这只适合等待时间较短的情况。还有,如果你使用 M2,M3的话,你需要定义为 volatile 。

Launcher | 园豆:45045 (高人七级) | 2013-09-17 10:23

@Launcher: 这点我知道,所以我改进成了使用异步委托+回调函数的方式。

private static volatile bool _m2;
        private static volatile bool _m3;
        private static void Main(string[] args)
        {
            DateTime dateTime = DateTime.Now;
            Console.WriteLine(dateTime);
            CheckUser("zong","zong");//第一步 验证用户
            Action step2 = delegate
                {
                    GetDatFromDb();//从数据库获取数据
                    _m2 = true;//标志位置为true
                };
            Action step3 = delegate
                {
                    GetDataFromWeb();//web服务获取数据
                    _m3 = true;//标志位置为true
                };
            step2.BeginInvoke(delegate
                {
                    if (_m2 && _m3)//通过标志位判断2 3是否都已完成
                    {
                        StartProcess(dateTime);//执行4
                    }
                }, null);
            step3.BeginInvoke(delegate
            {
                if (_m2 && _m3)//通过标志位判断2 3是否都已完成
                {
                    StartProcess(dateTime);//执行4
                }
            }, null);
           
            Console.Read();
        }
林J | 园豆:202 (菜鸟二级) | 2013-09-17 10:39

@林J: 你这个程序很巧妙就在于,你用了 Console.Read(); 所以省去了循环检测 m2,m3 的代码。使用 Console.Read 和同使用 Thread join 是一样的。但是你的代码有个BUG,就是可能执行两次 StartProcess(dateTime)。你可以自己分析下。

Launcher | 园豆:45045 (高人七级) | 2013-09-17 10:54

@Launcher: Console.Read可以看成是主线程自己要执行的其他事情,使用Thread Join的话,就会造成主线程阻塞了,这两者是不一样的,事实上检测 m2,m3的状态只会在进入BeginInvok的回调函数的时候检测一次而已,总共两次。当GetDataFromWeb()和StartProcess(dateTime)所花费时间非常相近的时候,StartProcess可能会执行两次,这我已经想到过了,可以通过在进入回调函数之后重置标志位+lock解决。

林J | 园豆:202 (菜鸟二级) | 2013-09-17 11:26

@林J: 

            step2.BeginInvoke(delegate
                {
                    if (_m2 && _m3)//通过标志位判断2 3是否都已完成
                    {
                        lock (obj)
                        {
                            _m2 = false;
                            if (_m3)
                                StartProcess(dateTime);//执行4
                        }
                    }
                }, null);
            step3.BeginInvoke(delegate
            {
                if (_m2 && _m3) //通过标志位判断2 3是否都已完成
                {
                    lock (obj)
                    {
                        _m3 = false;
                        if (_m2)
                            StartProcess(dateTime); //执行4
                    }
                }
            }, null);

重置标志位+lock 不过这样看来实现起来太过复杂 还是你提供的两种方法最实用。

林J | 园豆:202 (菜鸟二级) | 2013-09-17 11:31

@林J: 你的 Console.Read 后面必须接收一个 std 输入才能继续执行,但是在 main 退出前,你总是要等待 2,3 执行完成。单从你这段代码看,没觉得有啥区别,除非你能把 Console.Read 后面的还有同步执行的代码。Console.Read 同样是等待 std handle 上的一个事件被激活,也就是说,你仍然使用了 event 来同步,只是你不知道,如果将此程序放置在非 Console 程序中,你就应该明白。

如果你采用 lock,那么你同样需要一个全局锁,这样你就有三个变量 m2,m3,lock,如果使用 event,你需要一个 event 变量,在 step3 中 set event,然后在 step4 回调中 wait event,当然,这两个顺序可以颠倒,不影响结果。

当然上面的建议,并没涉及到使用异步IO来提高性能。

Launcher | 园豆:45045 (高人七级) | 2013-09-17 11:42

@Launcher: 

这跟Console.Read一点关系都没吧,在上述代码中,step2 step3 step4分别在不同线程内执行,主线程一直无阻塞,可以把Console.Read换成

while (true)
            {
                Console.WriteLine("do other thing" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(200);
            }

假如实用Thread Join模式,线程肯定会有阻塞的。

林J | 园豆:202 (菜鸟二级) | 2013-09-17 11:59

@林J: 那你就按你的思路去理解吧。

Launcher | 园豆:45045 (高人七级) | 2013-09-17 13:09
其他回答(1)
0

亲,有好几个办法可以解决你的问题,涉及到线程同步的

你可以去MSDN下面看一下Thread下面的方法,或是System.Threading命名空间下的类,就可以轻松解决你的问题.

只会造轮子 | 园豆:2274 (老鸟四级) | 2013-09-17 09:54
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册