Blog


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

  • 站点地图

  • 公益404

  • 搜索

Task取消

发表于 2019-11-05 | 分类于 线程 | 阅读次数:
字数统计: 476

一:在Thread中做取消操作

声明一个变量取去判断Thread中是否可以退出。

1
2
3
4
5
6
7
8
9
10
11
12
bool res = false;
var task = new Task(()=> {
while (!res)
{
Thread.Sleep(100);
Console.WriteLine("当前Thread={0}正在运行",Thread.CurrentThread.ManagedThreadId);
}
});
task.Start();
Thread.Sleep(1000);
res = true;
Console.Read();

1572915945633

不能让多个线程操作一个共享变量,否则会在release版本中有潜在bug

二:task中有专门的类CancellationTokenSource去处理“任务取消”

1.CancellationTokenSource远比一个变量强的多。

 CancellationTokenSource source = new CancellationTokenSource();
    var task = new Task(() => {
while (!source.IsCancellationRequested)
    {
        Thread.Sleep(100);
         Console.WriteLine("当前Thread={0}正在运行",Thread.CurrentThread.ManagedThreadId);
    }
 },source.Token);
task.Start();
Thread.Sleep(1000);
source.Cancel();

1572916271694

效果同上

2.当任务取消的时候,我希望有一个函数能够被触发,这个触发可以做一些资源的清理,又或者更新数据库信息

   CancellationTokenSource source = new CancellationTokenSource();
    source.Token.Register(()=> {
        //如果当前token被取消,此函数被执行
        Console.WriteLine("当前想线程被取消,可以做资源清理!");
    });
var task = new Task(() => {

    while (!source.IsCancellationRequested)
    {
        Thread.Sleep(100);
         Console.WriteLine("当前Thread={0}正在运行",Thread.CurrentThread.ManagedThreadId);
    }
},source.Token);
task.Start();
Thread.Sleep(1000);
source.Cancel();

1572916548220

3.延时取消:我想2秒之后自动取消,或者N秒

  • source.CancelAfter(new TimeSpan(0,0,0,2));

  • CancellationTokenSource 构造函数: CancellationTokenSource source = new CancellationTokenSource(1000);

  • 取消组合,将CancellationTokenSource 组成一个链表,其中任何一个CancellationTokenSource 被取消,组合也会被取消。var s3=s1&s2;

1
2
3
4
5
6
7
8
CancellationTokenSource source1 = new CancellationTokenSource();
source1.Cancel();
CancellationTokenSource source2 = new CancellationTokenSource();

var source3 = CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token);


Console.WriteLine("s1={0},s2={1},s3={2}",source1.IsCancellationRequested,source2.IsCancellationRequested,source3.IsCancellationRequested);

加了source1.Cancel();

不加source1.Cancel();

1572917999533

4.ThrowIfCancellationRequested

        CancellationTokenSource source1 = new CancellationTokenSource();
source1.Token.Register(()=> {
               Console.WriteLine("当前想线程被取消,可以做资源清理!");
           });
       var task = Task.Factory.StartNew(() =>
       {
           while (true)
           {
               source1.Token.ThrowIfCancellationRequested();
               Thread.Sleep(100);
               Console.WriteLine("当前Thread={0}正在运行", Thread.CurrentThread.ManagedThreadId);
           }
       }, source1.Token);
       Thread.Sleep(1000);
       source1.Cancel();

       Thread.Sleep(100);
       Console.WriteLine(task.Status);

1572919032504

Task的任务延续

发表于 2019-11-04 | 分类于 线程 | 阅读次数:
字数统计: 710

一:ContinueWith

1.Task=>Task.CreationOptions 第一task

2.Task=>TaskContinuationOptions 第二个task

第二个task需要判断第一个task在什么情况下,我该执行或者不该执行。。

二:LazyCancellation

1.Cancellation判断任务的取消。。。相当于Thread about

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CancellationTokenSource source = new CancellationTokenSource();//判断任务延续
source.Cancel();
Task task1 = new Task(() => {
//Thread.Sleep(1000);
Console.WriteLine("task1, tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"));
});
var task2 = task1.ContinueWith(t => {
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"));
}, source.Token);
var task3 = task2.ContinueWith(t => {

Console.WriteLine("task3,tid={0},dt={1},task2={2}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"), task2.Status);
});
task1.Start();

1572882705464

正常执行顺序:task1->ContinueWith task2->ContinueWith task3

结果执行顺序: task3->task1

解释:ContinueWith的时候,预先判断了source.Token的值,结果发现任务已经取消,这个时候,task2和task1没有延续的关系,所以task3和task1可以并行,看似ContinueWith的关系得不到延续【并行】

1572882940405

使用TaskContinuationOptions.LazyCancellation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CancellationTokenSource source = new CancellationTokenSource();//判断任务延续
source.Cancel();
Task task1 = new Task(() => {
//Thread.Sleep(1000);
Console.WriteLine("task1, tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"));
});
var task2 = task1.ContinueWith(t => {
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"));
}, source.Token,TaskContinuationOptions.LazyCancellation, TaskScheduler.Current);
var task3 = task2.ContinueWith(t => {
Console.WriteLine("task3,tid={0},dt={1},task2={2}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"), task2.Status);
});
task1.Start();
Console.Read();

1572883135949

TaskContinuationOptions.LazyCancellation当task2被取消了,还是先执行等待线程执行完成,在执行后面的线程

三:ExecuteSynchronously

同步执行任务,2个线程用同一个线程执行,可以减少时间片切换

1
2
3
4
5
6
7
8
9
10
11
12
Task task1 = new Task(() => {
Thread.Sleep(1000);
Console.WriteLine("task1, tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"));
});
var task2 = task1.ContinueWith(t => {
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"));
},TaskContinuationOptions.ExecuteSynchronously);
var task3 = task2.ContinueWith(t => {
Console.WriteLine("task3,tid={0},dt={1},task2={2}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HH:mm:ss"), task2.Status);
});
task1.Start();
Console.Read();

1572883421282

上图使用TaskContinuationOptions.ExecuteSynchronously,task1和task2使用同一个线程执行任务。

不使用TaskContinuationOptions.ExecuteSynchronously的结果:

1572883467747

四:NotOnRanToCompletion和OnlyOnRanToCompletion

前者表示延续任务必须在非完成状态才能执行

后者表示延续任务必须在完成状态才能执行

五:NotOnFaulted和OnlyOnFaulted

前者表示延续任务必须在非失败状态才能执行

后者表示延续任务必须在失败状态才能执行

六:NotOnCanceled和OnlyOnCanceled

前者表示延续任务必须在非取消状态才能执行

后者表示延续任务必须在取消状态才能执行

Task常见的枚举

发表于 2019-11-04 | 分类于 线程 | 阅读次数:
字数统计: 361

一:Task中的枚举

None = 0,
PreferFairness = 1,
LongRunning = 2,
AttachedToParent = 4,
DenyChildAttach = 8,
HideScheduler = 16,
RunContinuationsAsynchronously = 64

二:任务延续中的枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
None = 0,
PreferFairness = 1,
LongRunning = 2,
AttachedToParent = 4,
DenyChildAttach = 8,
HideScheduler = 16,
LazyCancellation = 32,
RunContinuationsAsynchronously = 64,
NotOnRanToCompletion = 65536,
NotOnFaulted = 131072,
NotOnCanceled = 262144,
OnlyOnRanToCompletion = 393216,
OnlyOnFaulted = 327680,
OnlyOnCanceled = 196608,
ExecuteSynchronously = 524288

三:演示

TaskCreationOptions:

1.AttachedToParent:子线程和父线程同步

Task task = new Task(() =>
        {

            Task task1 = new Task(() =>
            {
                Thread.Sleep(100);
                Console.WriteLine("task1");

            }, TaskCreationOptions.AttachedToParent);

            Task task2 = new Task(() =>
            {
                Thread.Sleep(10);
                Console.WriteLine("task2");
            }, TaskCreationOptions.AttachedToParent);
            task1.Start();
            task2.Start();
        });
        task.Start();
        task.Wait();
        Console.WriteLine("我是主线程");

1572837904340

2.DenyChildAttach:不让子任务附加到父任务中

Task task = new Task(() =>
{

    Task task1 = new Task(() =>
    {
        Thread.Sleep(100);
        Console.WriteLine("task1");

    }, TaskCreationOptions.AttachedToParent);

    Task task2 = new Task(() =>
    {
        Thread.Sleep(10);
        Console.WriteLine("task2");
    }, TaskCreationOptions.AttachedToParent);
    task1.Start();
    task2.Start();
}, TaskCreationOptions.DenyChildAttach);
task.Start();
task.Wait();
Console.WriteLine("我是主线程");

1572838075737

3.HideScheduler:子任务默认不调用父类的Task的Scheduler,而是使用默认的

4.LongRunning:长时间运行的任务,建议使用此选项,或者建议使用Thread而不是ThreadPool,如果长期租用不还给ThreadPool,ThreadPool会开启新的线程,如果此时租用线程归还,这回导致ThreadPool线程过多,销毁和调度都是一个很大的麻烦。

Task task1 = new Task(() =>
    {
        Thread.Sleep(100);
        Console.WriteLine("task1");
    }, TaskCreationOptions.LongRunning);
    task1.Start();
    task1.Wait();
    Console.WriteLine("我是主线程!!!");
    Console.Read();

1572838876190

5.PreferFairness:会将Task放入到ThreadPool的全局变量中,让word thread进行争抢,默认情况会放到task的一个本地队列中。

Task7种阻塞方式

发表于 2019-11-04 | 分类于 线程 | 阅读次数:
字数统计: 351

一:Task的阻塞和延续操作

1.阻塞

在Thread=>使用Join方法阻塞

Thread:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Thread t = new Thread(() =>
{
Thread.Sleep(1000);
Console.WriteLine("我是工作线程1");
});

Thread t2 = new Thread(() =>
{

Thread.Sleep(1000);
Console.WriteLine("我是工作线程2");
});
t2.Start();
t.Start();

t2.Join();
t.Join();

每等待一个,需要Join()一下,线程多了以后,就繁琐了!

Task:

1
2
3
4
5
6
7
8
9
10
11
Task task1 = new Task(()=> {
Thread.Sleep(1000);
Console.WriteLine("我是工作线程1:{0}", DateTime.Now.ToString("hh:mm:ss"));
});
task1.Start();
Task task2 = new Task(() => {
Thread.Sleep(2000);
Console.WriteLine("我是工作线程2:{0}", DateTime.Now.ToString("hh:mm:ss"));
});
task2.Start();
Task.WhenAll(task1, task2);

2.Task.WaitAll():是一种&&关系[必须其中所有的Task完成才算完成]

3.Task.WaitAny():是一种||关系[其中有一个Task完成就算完成]

4.Task.Wait()==join()

5.Task.WhenAll(task1,task2).ContinueWith();执行延续任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Task task1 = new Task(()=> {
Thread.Sleep(1000);
Console.WriteLine("我是工作线程1:{0}", DateTime.Now.ToString("hh:mm:ss"));
});
task1.Start();
Task task2 = new Task(() => {
Thread.Sleep(2000);
Console.WriteLine("我是工作线程2:{0}", DateTime.Now.ToString("hh:mm:ss"));
});
task2.Start();
//------------------------------------上面的等待返回值都是void
Task.WhenAll(task1, task2).ContinueWith(t=> {
//执行工作线程3
Console.WriteLine("我是主线程:{0}", DateTime.Now.ToString("hh:mm:ss"));
});
Console.Read();

1572830032942

6.Task.Factory.ContinueWhenAll();

7.Task.Factory.ContinueWhenAny();

Task

发表于 2019-11-04 | 分类于 线程 | 阅读次数:
字数统计: 370

一:Task(在.net 4.0版本使用)

  • Task=>Thread+ThreadPool
  • Thread:容易造成时间+空间开销,而且使用不当,容易造成线程过多,导致时间片切换
  • ThreadPool:控制能力比较弱,做Thread的延续,阻塞,取消,超时等等功能。控制权在CLR,而不是在我们手上。

Task是基于ThreadPool的基础上进行的封装,.net4..0之后,微软主推Task做异步启动。

二:Task的启动方式

1.实例化启动

1
2
3
4
5
Task task = new Task(() =>
{
Console.WriteLine("我是工作线程,tid={0}", Thread.CurrentThread.ManagedThreadId);
});
task.Start();

2.工程模式(Task.Factory启动)

var task = Task.Factory.StartNew(() =>
{
    Console.WriteLine("我是工作线程,tid={0}", Thread.CurrentThread.ManagedThreadId);
});

3.使用Task的Run方法

1
2
3
4
var task = Task.Run(() =>
{
Console.WriteLine("我是工作线程,tid={0}", Thread.CurrentThread.ManagedThreadId);
});

4.同步(阻塞线程,同步执行)

1
2
3
4
5
var task = new Task(() =>
{
Console.WriteLine("我是工作线程,tid={0}", Thread.CurrentThread.ManagedThreadId);
});
task.RunSynchronously();

三:Task是建立在ThreadPool

实例化Task在Windgb下的状态:

1572827768283

同步(阻塞线程,同步执行)在Windbg下的状态:

1572827872739

很明显看出:

  • Task的底层就是ThreadPool,ThreadPool分配了2个线程,属于CLR所有
  • 同步就没有其他线程,当线程执行完,才执行主线程

Task底层都是由不同的TaskScheduler支撑的

TaskScheduler相当于Task的CPU处理器

默认的TaskScheduler是ThreadPoolTaskScheduler

四:Task具有返回值

var task = new Task<string>(() =>
{
    return "Hello Word!";
});
task.Start();
Console.WriteLine(task.Result);

1572828794968

Thread定时器

发表于 2019-11-03 | 分类于 线程 | 阅读次数:
字数统计: 323

一.定时器 (可以想到Time)

1.ThreadPool的定时器功能:

1
2
3
4
ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(true), new WaitOrTimerCallback((obj, b) =>
{
Console.WriteLine("obj={0},tid={1},datetime={2}", obj, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("hh:MM:ss"));
}), "hello word", 1000, false);

RegisterWaitForSingleObject():

1.(new AutoResetEvent(true):两个线程之间进行信号发送,当true无需等待,当为false,会等待1秒

False:

1572776249485

True:

2.new WaitOrTimerCallback():回调函数

3.”hello word”:传递参数

4.1000:定时器执行的间隔(单位毫秒)

5.false:是否仅执行一次

6.debug执行的效果如下:

2.Timer

有很多地方都有Timer:

  • System.Threading
  • System.Timer
  • System.Windows.Form
  • System.Web.UI
1
2
3
4
Timer timer = new Timer(new TimerCallback((obj) =>
{
Console.WriteLine("obj={0},tid={1},datetime={2}", obj, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("hh:MM:ss"));
}), "hello word", 1000, 1000);

1.new TimerCallback();回调函数

2.“hello word”:传递参数

3.1000:延迟执行的时间【单位毫秒】

4.1000:线程执行的时间间隔

Timer首先是用ThreadPool.UnsafeQueueUserWorkItem(waitCallback, timer);来完成定时功能。

三:实战开发中,基本上不会用Timer来处理问题

功能太少。

比如,每天早上8点启动一个线程,逻辑太多计算时间,开销大,费时费力。

四:开源框架解决定时的问题:Quarz.net

ThreadPool

发表于 2019-11-03 | 分类于 线程 | 阅读次数:
字数统计: 461

一:Thread是CLR表示线程的数据结构

二:ThreadPool线程池

  • Thread:做一个异步任务,就需要开启一个Thread.[专有性]
  • ThreadPool:想做异步任务,打个比方,租车公司:租完车需要归还。

三:ThreadPool的使用方式

1
2
3
4
5
6
ThreadPool.QueueUserWorkItem((obj)=> {
var func = obj as Func<string>;
Console.WriteLine("我是工作线程:{0},content={1}",Thread.CurrentThread.ManagedThreadId,func());
},new Func<string>(()=>"Hello World"));

Console.WriteLine("主线程ID:{0}",Thread.CurrentThread.ManagedThreadId);

四:Thread和ThreadPool到底多少区别

1.如果现在有10个任务:

  • Thread:需要开启10个任务
  • Thread:10个任务放进线程池

2.区别

Thread:

线程数量:12个,未启动线程:0个,后台线程:1个,待处理线程:0个,死线程:10个。

XXXX表示线程死了,会进到终结器,GC会在下一次回收,会占用1M的堆栈空间。

执行命令:

!FinalizeQueue

可以看到有11线程进入终结器

ThreadPool:

ThreadCount:一共有6个线程,一个主线程,一个寄存器,4个工作线程

Cpu利用率30%,工作线程数量4个,在运行的线程为0,空闲的线程是4个,最大的线程是2047,最小的是4个

工作线程的队列是0个

IO线程端口线程0个,空闲0个,最大空闲8个,最大线程1000个,最小线程4个。

当前ThreadPool中,有“工作线程”和“IO线程”

工作线程:给一般的异步任务执行的。其中 不涉及网络,文件等IO操作。【开发调用】

IO线程:一般用在文件和网络IO上。【CLR调用】

3.总结

1.ThreadPool可以用8个线程解决10个线程干的事情:

节省了空间和时间:

时间:通过各个托管和非托管的dll

空间:teb,osthread结构,堆栈

Thread静态方法

发表于 2019-10-31 | 分类于 线程 | 阅读次数:
字数统计: 140

.Thread静态方法

一.Thread静态方法

1.主线程上设置槽位,也是Hello Word!只能被主线程读取,其他线程无法读取

Thread.AllocateDataSlot();分配未命名的槽位

var solt =Thread.AllocateNamedDataSlot(“username”);分配命名的槽位

Thread.SetData(solt, “Hello Word!”);设置槽位的值

var obj2 = Thread.GetData(solt);获取槽位的值

Thread.FreeNamedDataSlot(“username”);释放槽位

2.[ThreadStatic]:设置线程字段的唯一
3.ThreadLocal:提供线程的本地存储线程可见
4.TLS:thread local storage[线程本地存储]

二.Thread静态方法【内存栅栏】

1.Thread.MemoryBarrier();

Thread实例方法

发表于 2019-10-29 | 分类于 线程 | 阅读次数:
字数统计: 84

Thread实例方法

1.Thread的生命周期(join阻塞主线程)

  • Start():启动。
  • Suspend():暂停。
  • Resume():挂起。
  • Interrupt():中断(用来中断处于出于WaitSleepJoin状态的线程),相当continue;。
  • About():中断线程(通过抛出异常的销毁一个线程),相当于break
  • Join:阻塞主线程,等待子线程执行完后。

2.

Thread基础

发表于 2019-10-28 | 分类于 线程 | 阅读次数:
字数统计: 390

Thread的基础知识

一.空间的开销

1.Thread可以理解为操作系统的概率

Thread的内核数据结构,其中有osid,context=>cpu寄存器的里面的一些变量.

​ 时间片切换,切换间隔30ms

2.Thread环境块

TLS[thread本地存储],ExecptionList的信息

工具:WinDbg

功能:查看底层,线程的信息,如主线程,结束线程,OSID,终结器等!

命令:

  • .loadby sos clr:对于.Net Runtime 4.0,加载所有的DLL
  • !threads: 查看托管线程
  • !teb:列出结构_TEB的成员信息
  • !dumpdomain:查看程序域

3.用户模式堆栈:内存溢出的一个异常【堆栈溢出】

​ 一个线程分配1m的堆栈空间,存放【参数,局部变量】

4.内核模式堆栈

​ 在CLR的线程操作,包括线程同步,大多数调用底层的Win32函数,用户模式的参数需要传递到内核模式。

二.时间开销

1.我们的Application启动的时候,回加载很多的dll【托管和非托管】,exe,资源,元数据

进程启动时候为什么我没看到应用程序域?

答:进程启动回默认启动3个应用程序域。System domain,shared domain,domain


开启一个thread,销毁一个thread都会通知进程中的dll,attach,detach标志位

通知dll的目的就是给thread做准备工作,比如销毁,让这些dll做资源清理

2.时间片切换

4个逻辑处理器,可提供4个thread并行执行

执行5个thread,就有个一个thread休眠30ms

1…456

张聪

60 日志
6 分类
11 标签
© 2020 张聪
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
总访客 人 总访问量 次