《CLR via C#》读书笔记-.NET多线程(六)

parallel处理
当存在以下情况:
1、需处理多个独立方法
2、各方法之间不存在共享资源的情况
3、各方法可以使用相同的委托
就可以使用Parallel类的相关方法进行处理
以下是官网上的一个例子,

using System.Threading.Tasks;   
class Test
{
    static int N = 1000;

    static void TestMethod()
    {
        // Using a named method.
        Parallel.For(0, N, Method2);

        // Using an anonymous method.
        Parallel.For(0, N, delegate(int i)
        {
            // Do Work.
        });

    //Using ForEach
    Parallel.ForEach(collection,item=>DoWork(item));

        // Using a lambda expression.
        Parallel.For(0, N, i =>
        {
            // Do Work.
        });
    }

    static void Method2(int i)
    {
        // Do work.
    }
}

个人认为在这功能有点语法糖感觉。原因有两个:
原因一:从这个语法的本身而言,其使用多线程处理操作,本身就会消耗资源,因此parallel类更适合处理较复杂、耗时较长的操作。范围缩小了!
原因二:即使处理较复杂、耗时较长的任务,在业务上也大多是使用同一数据库事务,这样就能保证要么全成功要么全失败。而使用parallel类出现部分失败时,对于业务而言就比较困难了。
综上,个人认为对于parallel适应的范围不是很大。
定时器
定时器需要好好的写写!我的业务程序有一需求,就是比较频繁的定时查找数据,并将数据打印出来!之前使用WinForm界面上的timer,结果悲剧,当处理大量数据时,会存在两个问题:1、界面卡死;2、相同的内容会打印多遍(一般会打印2遍)。这两个问题那段时间经常被业务部门投诉!后来使用了多线程的定时器,解决了这个问题。
System.Threading.Timer类的构造函数如下所示:

public Timer(TimerCallback callback,object state,int dueTime,int period)

Timer构造器中四个参数的的定义分别如下:
callback的委托定义如下

public delegate void TimerCallback(
    object state
)

state为callback的参数值,若为空,可为null;
dueTime为从准备到执行的时间
period为时间每次操作的时间间隔
因此timer的一般使用方式如下:

//以下代码为伪码

private system.threading.timer doSomeThingTimer=null;

//开始执行定时操作的button按钮
public void button_click(e)
{
    doSomeThingTimer = new system.threading.timer(doSomeWork,null,5000,timeout.infinite);
}

//具体的业务逻辑方法
private void doSomeWork(object status)
{
    //业务逻辑代码

    //这儿一定要使用change方法,改变定时操作
    doSomeThingTimer.change(8000,timeout.infinite);
}

以上就是使用timer的具体方法,其中doSomeWork方法中使用了change方法。原因如下:若不使用change方法,而是在timer中定义好操作每次的执行间隔,则会出现以下情况。若操作的时间很长,超过了每次的执行间隔,则线程池就会调用额外的线程去执行操作,相当于有两个线程分别去执行doSomeWork。因此,为了避免这种情况的发生,需要在操作中使用change方法。
Timer小结
1、system.threading.timer类是使用线程池线程,其内部使用了Threadpool.queueUserWorkItem()方法。这也是为何timer的委托与queueUserWorkItem的委托一致的原因。
2、在线程池内部,线程池为所有的system.threading.timer共同使用一个线程。若一个不够用,则会额外再开立新的线程
3、system.windows.forms.timer中也有一个定时器,但该定时器属于UI线程,实UI线程有一消息泵,定时启动该定时器。这个定时器适合用于非常简单、耗时短、更新界面相关的操作。用于后台的或耗时的操作,请不要使用
4、system.windows.threading.dispatcherTimer类是system.windows.forms.timer在wpf和silverlight的等价物
5、system.timers.timer类。这个类很有意思,它是一个控件,当定时触发时,它会调用CLR的线程池线程进行操作。按理说它是正合适,但是它也有历史,它是不应该存在的。因为在微软大规模整理线程和定时器相关的方法之前,就把它留在了FCL中,因此由于历史原因,它也就没被删除。但平时尽量不要使用它。
线程池相关
当前线程池的上限是1000,一般情况下不要更改线程池的任何限定。
线程池原理
当使用threadpool.queueuserworkitem()及system.threding.timer创建的工作项会存放到上图中的全局队列中。然后线程池中的线程(也就是图中的工作者线程),会按照FIFO(先入先出,队列)的原则从全局队列中获取工作项,以进行完成。在这个过程中全局队列有一个同步锁,防止多个线程抢夺一个工作项。当工作者线程选取工作项后,全局队列就会将该工作项在列表中删除。
若此时,创建了一个task,则线程池会将task工作项放置在上图中的本地队列中。从而工作者线程就会优先到本地队列中获取工作项。选取方式与全局队列有区别,是后入先出的原则(即栈)获取工作项。若本地队列中已经没了工作项,则线程会到其他本地队列中“偷取”工作项进行处理。若所有的本地队列中的工作项都处理完了,则工作者线程就到全局队列中获取工作项。若全局队列中的工作项都处理完了,则工作者线程就会睡眠,然后一定时间段后自然醒来,若发现还没有事情做,就自杀掉。
若是一个外部的非工作者线程(例如,window线程),则不管是Threadpool还是timer还是task,CLR都将其放入全局队列中,非工作者线程会从全局队列中获取工作项以进行工作。
伪共享
我的系统是64位的操作系统,这也就意味着,系统可以一次性读取64byte的数据,因此创建2个int32的数据时,很有肯能两个int32变量存储在一个缓存线里。因此有两个线程对两个变量进行多次操作的时候,就会因读取相同的内容而进行资源的争夺,因此会造成性能的降低。因此为了避免这种“伪共享”的情况导致的性能下降,可以采用一些attribute,使两个字段分配到两个缓存线中,这样就会使性能提升。
26章小结
主要就是讲述了task。
待解决问题
整理attribute
26章完

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值