【读书笔记】C#高档编制程序 第110一章 职责、线程和协同

private readonly object syncRoot = new object();

public void DoTheJob()
{
    for (int i = 0; i < 50000; i++)
    {
        lock (syncRoot)
        {
            sharedState.State += 1;
        }
    }
}

 

一、Parallel.For()方法的裁撤

 

 

一、争用标准化

 1 private static readonly object locker = new object();
 2 static void Main(string[] args)
 3 {
 4     var tf = new TaskFactory();
 5     Task t1 = tf.StartNew(TaskMethod, "使用TaskFactory");
 6  
 7     Task t2 = Task.Factory.StartNew(TaskMethod, "使用Task.Factory");
 8  
 9     var t3 = new Task(TaskMethod, "使用Task构造函数并启动");
10     t3.Start();
11  
12     Task t4 = Task.Run(() => { TaskMethod("运行"); });
13  
14     Console.ReadKey();
15 }
16 static void TaskMethod(object title)
17 {
18     lock (locker)
19     {
20         Console.WriteLine(title);
21         Console.WriteLine("任务Id:{0},线程Id:{1}", Task.CurrentId == null ? "no Task" : Task.CurrentId.ToString(), Thread.CurrentThread.ManagedThreadId);
22         Console.WriteLine("是否为线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread);
23         Console.WriteLine("是否为后台线程:{0}",Thread.CurrentThread.IsBackground);
24         Console.WriteLine();
25     }
26 }

经过任务,能够钦点在任务成功后,应初阶运转另3个特定职分。

 

贰、后台线程

 

 

private static readonly object locker = new object();

public void ChnageI(int i)
{
    lock (locker)
    {
        if (i == 0)
        {
            i++;
            Console.WriteLine(i == 1);
        }
        i = 0;
    }
}

 

 

 

Thread类的Join()会告1段落当前线程,等待进入的线程落成得了,此时线程状态为WaitSleepJoin。

Parallel.Invoke()方法运维传递1个Action委托数组,在里边可以钦定供给相互运营的主意。

为了使锁定机制允许锁定多少个读取器访问有个别能源,能够应用ReaderWriterLockSlim类。

.NET肆.五富含3个撤回架构,允许以专门的学业方法收回长日子运作的职责。打消架构基于合营行为,它不是强制的。长日子运作的天职会检查它是不是被撤消,并回到调节权。援助撤废的章程接受多少个CancellationToken参考。

一旦有几个前台线程在运作,应用程序的进程就在运作。倘若多少个前台线程在运行,而Main()方法截止了,应用程序的历程就依然是激活的,直到全部前台线程完结其任务落成。

二、使用Paralle.ForEach()方法循环

 

Thread.ResetAbort()方法可以让线程在触发ThreadAbortException极度后持续运转;

Parallel.For<string>(10,25,()=> {
    Console.WriteLine("初始线程{0},任务{1}",Thread.CurrentThread.ManagedThreadId,Task.CurrentId);
    return string.Format("线程Id"+ Thread.CurrentThread.ManagedThreadId);
},
(i,pls,str1)=> {
    Console.WriteLine("迭代顺序:【{0}】,线程初始化返回值:【{1}】,线程Id:【{2}】,任务Id:【{3}】",i,str1, Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
    Thread.Sleep(10);
    return string.Format("迭代顺序:"+i);
},
(str1)=> {
    Console.WriteLine("线程主体返回值:{0}",str1);
});

 

 

lock (syncRoot)
{
    //代码
}
//C#编译器将lock语句解析为
Monitor.Enter(syncRoot);
try
{
    //代码
}
finally
{
    Monitor.Exit(syncRoot);
}

在For()方法中,前多少个参数定义了循环的起始和终结,第拾1个参数是一个Action<int>委托,参数是循环迭代的次数。

static void Main(string[] args)
{
    var t2 = new Thread(ThreadMainWithParameter);
    t2.Start("参数字符串");

    Console.ReadKey();
}
static void ThreadMainWithParameter(object message)
{
    Console.WriteLine("运行主线程,接受参数:" + message.ToString());
}

WaitHandle是一个虚无基类,用于等待一个时限信号的装置。能够等待差别的功率信号,因为WaitHandle是叁个基类,能够从中派生一些类。

.NET
Framework提供了多少个Timer类,用于在有个别时间距离后调用有个别方法。System.Threading.Timer、System.Timers.提姆er、System.WIndows.Forms.Timer、System.Web.UI.Timer和System.Windows.Threading.Timer。

2、Interlocked类

bool lockTaken = false;
Monitor.TryEnter(syncRoot, 1000, ref lockTaken);
if (lockTaken)
{
    //获取锁后操作
    try
    {
        //代码
    }
    finally
    {
        Monitor.Exit(syncRoot);
    }
}
else
{
    //没有获取锁的操作
}

Parallel.For()方法能够提前甘休:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         for (int j = 0; j < 5; j++)
 6         {
 7             int numTasks = 20;
 8             var state = new SharedState();
 9             var tasks = new Task[numTasks];
10             for (int i = 0; i < numTasks; i++)
11             {
12                 tasks[i] = Task.Run(() => { new Job(state).DoTheJob(); });
13             }
14  
15             for (int i = 0; i < numTasks; i++)
16             {
17                 tasks[i].Wait();
18             }
19             Console.WriteLine("最后结果:{0}", state.State);
20         }
21         Console.ReadKey();
22     }
23 }
24 
25 public class SharedState
26 {
27     public int State { get; set; }
28 }
29 
30 public class Job
31 {
32     SharedState sharedState;
33     public Job(SharedState sharedState)
34     {
35         this.sharedState = sharedState;
36     }
37     public void DoTheJob()
38     {
39         for (int i = 0; i < 50000; i++)
40         {
41             sharedState.State += 1;
42         }
43     }
44 }

职分不肯定要使用线程池中的线程,也足以应用此外线程。

(二)同步职分

选取Interlocked类能够更加快。

 

Parallel类、Task类和Parallel
LINQ为多少并行性提供了成都百货上千支援。但是,那一个类不能够直接支持数据流的拍卖,以及互动转变数据。这种景色下,使用System.Threading.Tasks.Dataflow名称空间中的相关类来拍卖。

线程调整器采取运转后,线程转台退换为Running;

 

对于联合,Barrier类特别适用于其中办事有四个职分分支且之后又需求统一工作的气象。

(三)使用单独线程的任务

var t1 = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create<int, int>(10, 5));
t1.Start();
Console.WriteLine(t1.Result);
t1.Wait();
Console.WriteLine("任务结果:{0} {1}",t1.Result.Item1, t1.Result.Item2);

一如此前的撤销方式也得以用于职务。

Paraller类是对线程的1个很好的画个饼来解除饥饿,该类位于System.Threading.Tasks名称空间中,提供了多少和职务并行性。

var t1 = new Task(TaskMethod, "长时间运行任务", TaskCreationOptions.LongRunning);
t1.Start();

(五)线程池

9、Barrier类

动用lock语句,修改DoTheJob()方法,未来技艺收获不错的结果。

 

职分落成时,它能够把部分管用的情况消息写到共享对象中,那几个共享对象必须是线程安全的。另八个增选是运用重回有个别结果的职分,如Future它是Task类的3个泛型版本,使用那些类时,能够定义职务回到的结果的档案的次序。

Task t1 = new Task(DoOnFirst);
t1.Start();
Task t2 = t1.ContinueWith(DoOnSecond);
Task t3 = t1.ContinueWith(DoOnSecond);
Task t4 = t2.ContinueWith(DoOnSecond);
Task t5 = t3.ContinueWith(DoOnSecond, TaskContinuationOptions.OnlyOnFaulted);//第二个参数指t3在失败的情况下运行t5

 

 

 

Mutex(mutual exclusion,互斥)是.NET
Framework中提供跨多个线程同步访问的三个类。

1、lock语句和线程安全

暗许景况下,用Thread类创立的线程是前台线程,线程池中的线程是后台线程。Thread类创造线程时,能够安装IsBackground属性来明确创设前台还是后台线程。

鉴于多少个线程都在等待对方,就涌出了死锁,线程将有线等待下去。为了幸免那些难点,可以在应用程序的系统架构中,从一同头就规划好锁定的一一,也足认为锁定定义超时时间。

Thread类的Abort()方法,触发ThreadAbortException类型的相当,线程状态会成为AbortedRequested,要是未有重新设置终止则改为Aborted;

 

 1 static void Main(string[] args)
 2 {
 3     var parent = new Task(ParentTask);
 4     parent.Start();
 5     Thread.Sleep(2000);
 6     Console.WriteLine(parent.Status);
 7     Thread.Sleep(4000);
 8     Console.WriteLine(parent.Status);
 9     Console.ReadKey();
10 }
11 static void ParentTask()
12 {
13     Console.WriteLine("任务Id:"+Task.CurrentId);
14     var child = new Task(ChildTask);
15     child.Start();
16     Thread.Sleep(1000);
17     Console.WriteLine("父级子任务已开始运行");
18 }
19 static void ChildTask()
20 {
21     Console.WriteLine("子任务开始");
22     Thread.Sleep(5000);
23     Console.WriteLine("子任务结束");
24 }

(十)数据流

 

 

 

与排斥和功率信号量对象一样,事件也是一个系统范围内的能源共同方法。为了从托管代码中应用系统事件,.NET
Framework在System.Threading名称空间中提供了马努alReset伊夫nt、AutoReset伊夫nt、马努alReset伊夫ntSlim和Countdown伊夫nt类。

4、调整线程

 

 

(九)Timer类

二、Future——职分的结果

亟待专注的是:线程池中的全数线程都现在台线程,不可设置线程优先级、名称,随着前台线程的终止而终止,只适用于短期职务。

4、SpinLock结构

线程是程序中单独的指令流。

8、Events类

使用.NET四.5中的三个新措施CancelAfter,在500飞秒后裁撤标志。在For()循环的兑当代码内部,Parallel类验证CanceledToken的结果,并撤回操作。一旦打消操作,For()方法就抛出四个OperationCanceledException类型的百般。

 

一、给线程传递数据

bool createdNew;
var mutex = new Mutex(false, "MyMutex", out createdNew);
if (!createdNew)
{
    Console.WriteLine("每次只能启动一个应用程序");
    Environment.Exit(0);
}
Console.WriteLine("运行中...");

lock语句是设置锁定和清除锁定的1种简单方法。

5、WaitHandle基类

ParallelLoopResult result = Parallel.For(0, 10, i =>
{
    Console.WriteLine("当前迭代顺序:" + i);
    Thread.Sleep(10);//线程等待
});
TaskMethod("主线程调用");
var t1 = new Task(TaskMethod,"同步运行");
t1.RunSynchronously();

 

(八)同步

bool createdNew;
var mutex = new Mutex(false, "MyMutex", out createdNew);

3、Monitor类

 

给线程钦命优先级,就足以影响系统对线程的调治顺序。在Thread类中,能够安装Priority属性,以震慑线程的主干优先级。

 

举个例子职分的代码应该长日子运作,就相应选择TaskCreationOptions.LongRunning告诉职务调解器创造3个新线程,而不是使用线程池中的线程。

(六)Thread类

该类允许创造前台线程,以及安装线程的先行级。

Thread.Sleep()方法会使线程处于WaitSleepJoin;

读取Tread的ThreadState属性,能够获得线程的情事。

.NET④.5为确定性信号量功用提供了五个类Semaphore和塞马phoreSlim。Semaphore类可以命名,使用系统范围内的能源,允许在分裂进度之间联合。SemaphoreSlim类是对很短等待时间张开了优化的轻型版本。

2、死锁

二、职务的撤消

(四)撤废架构

具有必要等待的操作,举例,因为文件、数据库或网络访问都急需自然的日子,此时就足以运维一个新的线程,同时完毕其它职务。

(三)任务

 

 

var result = Parallel.For(10, 40, async (int i, ParallelLoopState pls) =>
 {
     Console.WriteLine("迭代序号:{0}, 任务: {1}, 线程: {2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
     await Task.Delay(10);
     if (i > 15)
     {
         pls.Break();
     }
 });
Console.WriteLine("循环完成状态:" + result.IsCompleted);
Console.WriteLine("Break索引:" + result.LowestBreakIteration);
static void Main(string[] args)
{
    var t1 = new Thread(ThreadMain) { Name = "MyNewThread", IsBackground = false };
    t1.Start();
    Console.WriteLine("主线程现在结束");
    Console.ReadKey();
}


private static void ThreadMain()
{
    Console.WriteLine(Thread.CurrentThread.Name+"线程开始运行");
    Thread.Sleep(3000);
    Console.WriteLine(Thread.CurrentThread.Name+"线程结束");
}

Interlocked类用于使变量的简练语句原子化。

Parallel类只等待它创立的天职,而不等待其余后台活动。

(壹)使用线程池的天职

为了更加好的主宰并行动作,能够选取System.Threading.Tasks名称空间中的Task类。

 

C#中event关键字与这里的event类没有其他涉及。

举例五个或多少个线程访问同1的对象,并且对共享状态的走访尚未同步,就能够并发争用标准。要防止该难点,可以锁定共享对象。那足以经过lock语句锁定在线程中国共产党享的state变量。

在通过Thread类创设线程的时候,设置IsBackground属性为false,也等于创设3个前台线程。这种情景下在主线程停止时,t一不会终止。但假诺将IsBackground设置为true,则会趁机主线程的结束而终止。

Thread的Start()方法成立线程,线程状态为UnStarted;

 

Monitor类相对于lock语句的独到之处是:能够透过调用TryEnter()方法增多1个等候被锁定的超时值。

亟需小心的是,Break()方法仅是报告循环在适龄的时候退出当前迭代之外的迭代。

叁、三番五次的任务

功率信号量是一种计数的互斥锁。假若急需限制能够访问可用财富的线程数,能量信号量就很有用。

Parallel.For()还足以对线程举行初叶化和退出时制定办法:

 

职责也得以组合叁个档期的顺序结构。一个职责运维三个新任务时,就运营了3个父/子等级次序结构。

 

 1 static void Main(string[] args)
 2 {
 3     Parallel.Invoke(Say1, Say2, Say3, Say4, Say5);
 4     Console.WriteLine("---------");
 5     Say1();
 6     Say2();
 7     Say3();
 8     Say4();
 9     Say5();
10  
11     Console.ReadKey();
12 }
13 static void Say1()
14 {
15     Thread.Sleep(100);
16     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + "1");
17 }
18 static void Say2()
19 {
20     Thread.Sleep(100);
21     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + "2");
22 }
23 static void Say3()
24 {
25     Thread.Sleep(100);
26     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + "3");
27 }
28 static void Say4()
29 {
30     Thread.Sleep(100);
31     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + "4");
32 }
33 static void Say5()
34 {
35     Thread.Sleep(100);
36     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + "5");
37 }

Paraller.For()和Paraller.ForEach()方法在历次迭代中调用一样的代码,二Parallel.Invoke()方法允许同时调用区别的方法。Paraller.Invoke用于职务并行性,而Parallel.ForEach用于数据并行性。

 

三、线程的先行级

 

public int State
{
    get
    {
        lock (this)
        {
            return ++state;
        }
    }
}

public int State
{
    get
    {
        return Interlocked.Increment(ref state);
    }
}

(二)Paraller类

lock语句由C#编写翻译器分析为运用Monitor类。

在Mutex类的构造函数中,能够钦定互斥是还是不是最初由主调线程具有,定义互斥的名称,获得互斥是或不是留存的音讯。

(一)概述

共享数据必须运用同步本领,确定保证1遍唯有二个线程访问和改换共享状态。能够行使lock语句、Interlocked类和Monitor类举行进度之中的一齐。Mutex类、伊芙nt类、SemaphoreSlim类和ReaderWriterLockSlim类提供了几个经过之间的线程同步。

 

 1 var cts = new CancellationTokenSource();
 2 cts.Token.Register(() => Console.WriteLine("*** token canceled"));
 3 
 4  
 5 //在500毫秒以后发送取消指令
 6 cts.CancelAfter(500);
 7 try
 8 {
 9     var result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x =>
10     {
11         Console.WriteLine("{0}次循环开始", x)
12         int sum = 0;
13         for (int i = 0; i < 100; i++)
14         {
15             Thread.Sleep(2);
16             sum += i;
17         }
18         Console.WriteLine("{0}次循环结束", x);
19     });
20 }
21 catch (OperationCanceledException ex)
22 {
23     Console.WriteLine(ex.Message);
24 }

3、通过Parallel.Invoke()方法调用七个章程

 

7、Semaphore类

 

(柒)线程难点

 

要是使用了ParameterizedThreadStart委托,线程的入口点必须有1个object类型的参数,且再次来到类型为void。

4、职务档案的次序结构

string[] data = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k" };
Parallel.ForEach(data, (s, pls, l) =>
{
    Console.WriteLine(s + " " + l);//s是当前循环的项的值,pls是ParallelLoopState类型,l是当前迭代的顺序
});

冲突于Monitor垃圾回收导致过高的种类开垦,使用SpinLock结构就会管用降低系统开辟。SpinLock的应用方法与Monitor极其相似,但因为SpinLock是组织所以在把变量赋值为另三个变量会创建三个别本。

1、Parallel.For()方法循环

一、运营职分

除去循环起来与甘休的钦点,第十个是对迭代调用的各种线程进行管理,第多个是迭代的秘诀主体,第9个是迭代到位时对线程的拍卖。

 1 static void Main(string[] args)
 2 {
 3     int taskCount = 6;
 4     int semaphoreCount = 3;
 5     var semaphore = new SemaphoreSlim(semaphoreCount, semaphoreCount);
 6     var tasks = new Task[taskCount];
 7 
 8  
 9     for (int i = 0; i < taskCount; i++)
10     {
11         tasks[i] = Task.Run(() =>
12         {
13             TaskMain(semaphore);
14         });
15     }
16 
17     Task.WaitAll(tasks);
18  
19     Console.WriteLine("所有任务已结束");
20     Console.ReadKey();
21 }
22 
23  
24 private static void TaskMain(SemaphoreSlim semaphore)
25 {
26     bool isCompleted = false;
27     while (!isCompleted)
28     {
29         if (semaphore.Wait(600))
30         {
31             try
32             {
33                 Console.WriteLine("任务{0}锁定了信号", Task.CurrentId);
34                 Thread.Sleep(2000);
35             }
36             finally
37             {
38                 Console.WriteLine("任务{0}释放了信号", Task.CurrentId);
39                 semaphore.Release();
40                 isCompleted = true;
41             }
42         }
43         else
44         {
45             Console.WriteLine("任务{0}超时,等待再次执行", Task.CurrentId);
46         }
47     }
48 }

系统可以分辨盛名称的排外,能够利用它来禁止应用程序运行一遍。

10、ReaderWriterLockSlim类

 1 var cts = new CancellationTokenSource();
 2 cts.Token.Register(() => Console.WriteLine("*** token canceled"));
 3 
 4 //在500毫秒以后发送取消指令
 5 cts.CancelAfter(500);
 6  
 7 Task t1 = Task.Run(()=> {
 8     Console.WriteLine("任务进行中...");
 9     for (int i = 0; i < 20; i++)
10     {
11         Thread.Sleep(100);
12         CancellationToken token = cts.Token;
13         if (token.IsCancellationRequested)
14         {
15             Console.WriteLine("已发送取消请求,取消请求来自当前任务");
16             token.ThrowIfCancellationRequested();
17             break;
18         }
19         Console.WriteLine("循环中...");
20     }
21     Console.WriteLine("任务结束没有取消");
22 });
23 try
24 {
25     t1.Wait();
26 }
27 catch (AggregateException ex)
28 {
29     Console.WriteLine("异常:{0}, {1}",ex.GetType().Name,ex.Message);
30     foreach (var innerException in ex.InnerExceptions)
31     {
32         Console.WriteLine("异常:{0}, {1}", ex.InnerException.GetType().Name, ex.InnerException.Message);
33     }
34 }
int nWorkerThreads;
int nCompletionPortThreads;
ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads);
Console.WriteLine("线程池中辅助线程的最大数目:{0}, 线程池中异步 I/O 线程的最大数目:{1}",nWorkerThreads,nCompletionPortThreads);
for (int i = 0; i < 5; i++)
{
    ThreadPool.QueueUserWorkItem(JobForAThread);
}
Thread.Sleep(3000);

6、Mutex类

在未有动用lock语句的景观下,多个线程操作共享数据,最后收获的结果尚未贰个会正确。

相关文章