.NET 使用WaitHandle开启并发多线程查询并同步返回

原因

最近在APP开发过程中,或者说在服务端的一些业务中,难免会遇到需要将一堆不同内容的查询结果放在一个list种进行返回,如果数据量大,查询内容多,对数据实时性要求高,仅仅是建立索引,或者建立服务器数据缓存机制,都不能从根本上解决单线程逐SQL语句查询时间漫长的问题。

串行查询

img1

联想

无论是java,C#,JavaScript或者在OC中,都有多线程这个东西存在,之前对于多线程运用在这种多任务的处理上并不多,但是不禁想到,是不是也有一种办法,让所有的查询在子线程中去执行,然后等待所有的子线程查询结束,并且将每个查询的结果值添加到list中后,再从主线程中将list返回呢,这样就可以利用多线程的优势,极大的提高查询的效率,几乎能将查询时间缩短到耗时最长的一个查询耗时上,并且现今的电脑都是多核CPU,使用多线程来解决这种问题似乎是再好不过了。

并发查询

iamge2

EventWaitHandle(等待事件句柄)

EventWaitHandle是一个在线程处理上的类,它可以和WaitHandle配合使用完成多线程任务等待调度,并且在主线程中统一处理想要的结果。

代码如下:

 private List<string> test()
        {
            List<string> list = new List<string>();
            //创建等待事件句柄集合
            var watis = new List<EventWaitHandle>();
            for (int i = 0; i < 5; i++)
            {
                //创建句柄   true终止状态
                var handler = new ManualResetEvent(false);
                watis.Add(handler);
                //将要执行的方法参数化
                ParameterizedThreadStart start = new ParameterizedThreadStart(selectData);
                //创建线程,传入线程参数
                Thread t = new Thread(start);
                //启动线程
                t.Start(new Tuple<int, EventWaitHandle, List<string>>(i, handler,list));
            }
            //等待句柄
            WaitHandle.WaitAll(watis.ToArray());
            return list;
        }

  • 首先创建了一个EventWaitHandle的list,这个list将用于来添加所有的需要执行的等待事件句柄

  • 然后将需要参与等待的任务(一个方法)参数化传入线程初始化的构造

  • 在线程启动时,将与之对应的EventWaitHandle子类ManualResetEvent的对象传入需要调用的任务(方法)中

  • 最后使用WaitHandle.WaitAll执行所有的等待事件句柄

 private static void selectData(object param)
        {
            Tuple<int, EventWaitHandle,List<string>> t = (Tuple<int, EventWaitHandle,List<string>>)param;
            Console.WriteLine(Thread.CurrentThread.Name + "执行开始");
            //sleep线程,模拟查询业务
            if (t.Item1 == 0)
            {
                Thread.Sleep(1500);
                t.Item3.Add("这是第0个线程添加的内容");
            }
            else if (t.Item1 == 1)
            {
                Thread.Sleep(1234);
                t.Item3.Add("这是第1个线程添加的内容");
            }
            else if (t.Item1 == 2)
            {
                Thread.Sleep(1759);
                t.Item3.Add("这是第2个线程添加的内容");
            }
            //将事件状态设置为有信号,从而允许一个或多个等待线程继续执行。
            t.Item2.Set();
            Console.WriteLine(Thread.CurrentThread.Name + "执行结束");
        }

  • 在等待句柄任务中执行查询,并将结果加入数据list中

  • 最后在任务的最后(执行完成)将等待事件句柄对象Set(),这个方法将发出一个信号(暂时理解为通知WaitHandle当前的等待事件句柄执行完成)

处理结果
img

可以看出耗费的时间基本上为耗时最长的那个任务的耗时,可以想象,如果是在做多查询结果集的时候,带来的速度上的提升肯定是成倍的增长。

实际运用效果

提示10倍以上速度

博主最近参与开发的APP中有一个接口是需要从两个不同的数据库中进行数据查询,其中需要对查询数据库1中的返回数据进行遍历,然后根据遍历获取的数据去查询数据库2中的数据,而查询数据库2中的数据一次耗时在170ms左右,一次查询10条数据,整个接口查询将近用时1800ms,经过优化后,使用多线程并发查询,查询时间缩短在200ms以内,提示的效果非常显著。

多线程调试是一个非常头疼的事情,断点并不会按照当前进入断点的线程走(会乱跳),因为是多线程进行,会按照CPU抢占的实际情况进断点,所以建议调试的时候使用debug输出调试,并且在多线程断点的时候其它未被断点的线程会执行完成,而你断点的线程却还停留在你断点的位置。

多线程并发查询还需要考虑数据库的锁问题,虽然数据库一般对查询语句不会加锁,然而博主在实际使用过程中,ADO.net似乎是对查询进行了加锁,让并发到数据库的时候变成了串行,这个时候可以在查询的sql语句中添加with nolock,当然,并发查询肯定也会被其它操作的锁所影响,如果是在查询过程中还进行了其它的操作,有可能会查询出脏数据。

思考

使用等待句柄以防止进程在等待后台线程未完成执行时终止,实现根据官网的文档说明是使用的代理模式,猜想在调用的Wait方法中,应该有存在类似while(true)这样的循环或者其它阻塞当前线程的方法存在,这个存在一直阻止着当前主线程的在Wait中不返回,直到等待句柄调用了set方法时发出信号通知WaitHandle,将Wait方法中累计while循环的结束的条件,当前所有的等待句柄都发出信号后,Wait方法中while循环终止条件满足,不再继续阻塞当前主线程进行返回。

有不对的地方,请指出。(END)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 143,396评论 1 301
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,482评论 1 258
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 94,858评论 0 213
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,131评论 0 179
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,903评论 1 256
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,847评论 1 177
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,454评论 2 273
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,206评论 0 167
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,047评论 6 232
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,563评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,344评论 2 215
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,667评论 1 231
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,264评论 0 32
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,163评论 2 214
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,546评论 3 207
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,630评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,032评论 0 166
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,572评论 2 231
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,668评论 2 232

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,016评论 1 32
  • Java-Review-Note——4.多线程 标签: JavaStudy PS:本来是分开三篇的,后来想想还是整...
    coder_pig阅读 1,572评论 2 17
  • 内存模型 五大区域 程序计数器(PC) 虚拟机栈 本地方法栈 方法区(包含常量池) 堆 详细介绍 1. 程序计数器...
    齐晋阅读 640评论 0 0
  • 读《你一年的8760小时》有感与反思 高中毕业一周年了,今天恰逢教师节,向往日老师发送了节日祝福,当然也和老师情不...
    帅boy帅阅读 213评论 0 1
  • 我在路上,遇见了繁花似锦和曲径寥籁 似锦的繁花无暇与我对视,寂寥的幽径无闲让我走过。 我只待繁花尽谢,草陌幽径。 ...
    菜瓜饭阅读 443评论 1 2