• C# 多线程ManualResetEvent、等待所有线程


    需求:成员A可能有几十个,我需要更新所有的A,然后根据A的数据,去更新成员B。

    解决方案:思路是想通过多线程更新所有的A,然后通过等待线程来确定所有的A是否都更新完,最后更新B。

    Member B = ....;//B成员的model
    IList<Member> list = ......;//查出所有的A成员,装进list里。
    
    ManualResetEvent[] manualEvents = new ManualResetEvent[list.Count];
    //更新所有的A成员
    for (int i = 0; i < list.Count; i++)
    {
        manualEvents[i] = new ManualResetEvent(false);//初始化的ManualResetEvent必须是false
        model = new te();//目前我只知道此处只能传一个参数,所以为了传递多个参数,我写了个model,然后把要参数都放进model里
        model.MRevent = manualEvents[i];
        model.member = list[i];//此处是我处理数据需要的参数,把成员的model传进去
        ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateMember), model);
    }
    //等待所有线程执行完毕
    WaitHandle.WaitAll(manualEvents);
    
    //更新B成员
    model = new te();
    model.member = B;
    model.MRevent = new ManualResetEvent(false);
    UpdateMember(model);

    处理数据的方法

    public void UpdateMember(object tsmodel)
    {
        te stateInfo = (te)tsmodel;
        Member member = stateInfo.member;//中间为处理数据更新成员数据
        ......
    
        stateInfo.MRevent.Set(); //将事件状态设置为终止状态,允许一个或多个等待线程继续(我个人理解是调用此方法,及为该线程结束,ManualResetEvent应该变成true)
    }

    新建立的model

    public class te
    {
        public Member member{ get; set; }
        public ManualResetEvent MRevent { get; set; }//小于64线程的时候使用
        public MutipleThreadResetEvent MTRevent { get; set; }//大于64线程的时候使用
    }

    此处出现了新问题:

    当线程大于64条时,会报错。应该是WaitHandle.WaitAll(manualEvents);这等待的线程数不能大于64。

    错误信息:waithandles 的数目必须少于或等于 64 个。

    经过网上查询,找到的解决办法,原文地址http://www.cnblogs.com/xiaofengfeng/archive/2012/12/27/2836183.html

    原理:封装一个ManualResetEvent对象,一个计数器current,提供SetOne和WaitAll方法;

    主线程调用WaitAll方法使ManualResetEvent对象等待唤醒信号;

    各个子线程调用setOne方法 ,setOne每执行一次current减1,直到current等于0时表示所有子线程执行完毕 ,调用ManualResetEvent的set方法,这时主线程可以执行WaitAll之后的步骤。

    目标:减少ManualResetEvent对象的大量产生和使用的简单性。

    /********************************************************************************
     * Copyright © 2001 - 2010Comit. All Rights Reserved.
     * 文件:MutipleThreadResetEvent.cs
     * 作者:杨柳
     * 日期:2010年11月13日
     * 描述:封装 ManualResetEvent ,该类允许一次等待N(N>64)个事件执行完毕
     * 
     *       解决问题:WaitHandle.WaitAll(evetlist)方法最大只能等待64个ManualResetEvent事件
     * *********************************************************************************/
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
     
    namespace TestMutipleThreadRestEvent
    {
        /// <summary>
        ///  封装ManualResetEvent
        /// </summary>
        public class MutipleThreadResetEvent : IDisposable
        {
            private readonly ManualResetEvent done;
            private readonly int total;
            private long current;
     
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="total">需要等待执行的线程总数</param>
            public MutipleThreadResetEvent(int total)
            {
                this.total = total;
                current = total;
                done = new ManualResetEvent(false);
            }
     
            /// <summary>
            /// 唤醒一个等待的线程
            /// </summary>
            public void SetOne()
            {
                // Interlocked 原子操作类 ,此处将计数器减1
                if (Interlocked.Decrement(ref current) == 0)
                {
                    //当所以等待线程执行完毕时,唤醒等待的线程
                    done.Set();
                }
            }
     
            /// <summary>
            /// 等待所以线程执行完毕
            /// </summary>
            public void WaitAll()
            {
                done.WaitOne();
            }
     
            /// <summary>
            /// 释放对象占用的空间
            /// </summary>
            public void Dispose()
            {
                ((IDisposable)done).Dispose();
            }
        } 
     
    }

    本质就是只通过1个ManualResetEvent 对象就可以实现同步N(N可以大于64)个线程

    修改后的方法:(黄色荧光笔的都是改动的代码)

    Member B = ....;//B成员的model
    IList<Member> list = ......;//查出所有的A成员,装进list里。
    //更所有A成员
    using (var countdown = new MutipleThreadResetEvent(list.Count))
    {
        for (int i = 0; i < list.Count; i++)
        {
            model = new te();
            model.MTRevent = countdown;
            model.member = list[i];
            //开启N个线程,传递MutipleThreadResetEvent对象给子线程
            ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateMember), model);
        }
    
        //等待所有线程执行完毕
        countdown.WaitAll();
    }
    //更新B成员
    using (var countdown = new MutipleThreadResetEvent(1))
    {
        model = new te();
        model.MTRevent = countdown;
        model.member = B;
        //开启N个线程,传递MutipleThreadResetEvent对象给子线程
        ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateMember), model);
        //等待所有线程执行完毕
        countdown.WaitAll();
    }

    处理数据的方法需要小改一下:

    public void UpdateMember(object tsmodel)
    {
        te stateInfo = (te)tsmodel;
        Member member = stateInfo.member;
        //中间为处理数据更新成员数据
        ......
    
        stateInfo.MTRevent.SetOne();//此处调用封装的方法
    }

    此时,大于64条线程的可以使用了。

  • 相关阅读:
    验证视图状态 MAC 失败的处理办法总结![come from internet]
    在ie6上 让他显示png图片
    挑灯夜战 css hack 再次学习 IE浏览器常见的9个css Bug以及解决办法
    fileupload上传 and IE8 and VS2008 实现图片预览[new Name:经典的Fakepath错误]
    大项目开发语言的选择
    虚拟机安装linux:未找到任何驱动程序
    objectc中的bool
    关于数字内容网站的一些想法
    iphone开发论坛
    在centos5.3下安装mysql5启动时报错
  • 原文地址:https://www.cnblogs.com/cang12138/p/6911912.html
Copyright © 2020-2023  润新知