C#   .net  Framework多线程演变路径:

1.0    1.1 时代使用Thread

2.0    时代使用ThreadPool

3.0    时代使用Task

4.0    时代使用Parallel

4.5 时代使用 async/awit

一.   DoSomethingLong方法如下:

/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomethingLong(string name)
{
Console.WriteLine($"****************DoSomethingLong {name} Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1000000000; i++)
{
lResult += i;
}
//Thread.Sleep(2000); Console.WriteLine($"****************DoSomethingLong {name} End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}

二.   使用Thread

a)         下面代码演示, 如何使用Thread来启动一个线程

 //public Thread(ThreadStart start, int maxStackSize);  //maxStackSize 表示指定这个线程最大可以使用多少内存空间, 一般不用设置
ThreadStart threadStart = () => this.DoSomethingLong("btnThreads_Click");
Thread thread = new Thread(threadStart);
thread.Start();

 b) Thread的一些其它api

i.	thread.Suspend();//(弃用)线程挂起, 使线程暂停执行; 已过期, 不推荐使用, 会导致死锁, 因为线程的执行的时候, 是会占用资源的, 虽然手动让线程暂停执行了, 但是它占用的资源是不会释放的
ii. thread.Resume();//(弃用)唤醒线程, 使挂起的线程重新开始执行, 和Suspend()对应
iii. thread.Abort(); //线程终止
try
{
thread.Abort();//销毁,方式是抛异常 也不建议再使用 不一定及时/有些动作发出收不回来(比如子线程向数据库发出一个查询命令, 此时命令已经发出, 但是数据库还没有返回来值, 但是主线程强制子线程停止了, 然后数据库返回来的值就没有人接收了)
//就像你正在跑步, 人后旁边有人说停; 你从听到停, 到真正的停下来, 还是处于跑的状态
// 使用abort一定要使用try catch来处理
}
catch (Exception)
{
//
Thread.ResetAbort();//取消Abort异常, 然后继续计算
} //线程等待
iv. thread.Join();//当前线程等待thread完成, 当前线程就是谁执行这句话, 谁就是当前线程, 在这里当前线程就是主线程了, 因为主线程在执行这句话
thread.Join(500);//最多等500; 当前线程等待500毫秒
Console.WriteLine("等待500ms"); // ThreadState.Running //启动线程
while (thread.ThreadState != ThreadState.Stopped)
{
Thread.Sleep(100);//当前线程 休息100ms
}
v. 关于jion和sleep
//jion是实实在在的等待, 占据cpu; 像上面的示例, thread.jion(500), 那么这个时候, 其实thread还是在做自己的事情. jion就会在这里等一段时间(或者等thread把活干完); 此时是有两个线程并发运行的; 一个thread的线程, 一个jion的线程
//sleep表示睡眠, 把当前线程的上下文保存着, 把cpu的时间片交出去, cpu可以去处理其他事情 vi. 前台线程和后台线程的区别
////Console.WriteLine(thread.IsBackground);
////默认是前台线程,启动之后一定要完成任务的,阻止进程退出; 也就是说, 就算你的应用程序被关闭了, 但是前台线程还是要坚持把它的事情做完之后才会停止自己
////thread.IsBackground = true;//指定后台线程:随着进程退出, 也就是说程序关闭后, 后台线程也停止了 vii. 线程优先级:
thread.Priority = ThreadPriority.Highest;//线程优先级
////CPU会优先执行标记为Highest的线程, 但是并不代表说Highest就一定最先把事情处理完, 只能说CPU会优先为这个线程分配时间片

三.   使用ThreadPool

a)         推出ThreadPool的原因:

i.	在thread中提供了太多的API, 又是挂起, 又是终止, 又是休眠, 又是优先级, 各种各样乱七八糟, 但是又不是真真正正的真的能准确的操控
ii. 于是就到2.0之后,Thread就被替换成了ThreadPool, 把所有的该简化的都简化了,什么API都没有了. 也没有销毁, 也没有挂起, 更没有暂停; 但是也可以将线程进行重用, 避免重复的创建和销毁, 线程被使用完之后, 会放回池子, 下次继续使用

b)         使用线程池ThreadPool的方式启动多线程, 代码如下:

//池→容器; 线程池就是线程的容器, 当在一个系统中, 反复的使用不同的资源的时候, 但是这些资源使用完需要二次创建(销毁)的成本太高的时候, 就要考虑使用池技术
//池技术就是享元模式的精华
//QueueUserWorkItem队列用户工作项, 将用户的工作项, 放入到队列汇总
//QueueUserWorkItem从线程池的方式来启动多线程; 这可能是启动多线程最简单的一种方式了
ThreadPool.QueueUserWorkItem(t => this.DoSomethingLong("btnThreadPool_Click"));
ThreadPool.QueueUserWorkItem(t => this.DoSomethingLong("btnThreadPool_Click"));

c)         设置/获取 ThreadPool中最大和最小线程数量:

 {
//对线程加以限制 workerThreads→线程池中最大的工作线程数据
//workerThreads →线程池中的最大线程数, 这个是工作线程, 平时启动的线程一般都是基于工作线程的, 如果超过了将会被排队
//completionPortThreads表示线程池中异步i/o线程的最大数目
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");
}
{
//线程池内最小线程数; 默认情况下, 最小的线程数, 好像是跟CPU有关; 比如4核8线程的, 那么这里就是8和8; 我的就是4和4
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"GetMinThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");
}
// 有get就有set 设置最大线程数 ThreadPool的最大线程数也会影响这Task的线程
ThreadPool.SetMaxThreads(16, 16);
// 设置最小线程数
ThreadPool.SetMinThreads(8, 8); //实际操作来看, 最小的会受到影响
//设置完成后, 再次打印一次
{
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");
}
{
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"GetMinThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");
}

 

d)         在ThreadPool中实现线程等待

////ThreadPool啥API都没有, 那么如何在ThreadPool中等待线程完成才往下执行呢?
// 可以使用 ManualResetEvent(手动重启事件)类, 这个类包含了一个bool属性, 如果在初始化这个类的时候, 将其初始化为false, 则这个类实例的的WaitOne()方法将会被阻塞, 一直阻塞, 直到他的bool属性变成true; 当然, 可以通过ManualResetEvent的set方法使其变成 true ; 当然这里也可以自己定义一个变量来实现, 但是ManualResetEvent是线程安全的
// false--WaitOne等待--Set--true--WaitOne直接过去
// true--WaitOne直接过去--ReSet--false--WaitOne等待
ManualResetEvent manualResetEvent = new ManualResetEvent(false); //初始化ManualResetEvent类中的变量为false
ThreadPool.QueueUserWorkItem(t =>
{
Console.WriteLine("即将开始执行DoSomethingLong函数");
this.DoSomethingLong("btnThreadPool_Click");
Console.WriteLine("执行DoSomethingLong函数完毕");
manualResetEvent.Set(); //将信号设置为true
//manualResetEvent.Reset(); //将信号设置为false
});
manualResetEvent.WaitOne(); //阻塞当前线程; 如果当前manualResetEvent在主线程创建的, 那么就会阻塞主线程 但是要注意一般来说,不要阻塞线程池的线程

  

四.    使用Thread完成回调和带返回值的回调

a)         使用Thread完成回调, 代码如下:

 /// <summary>
/// 演示线程的回调; 启动子线程计算--完成委托后,该线程去执行后续回调委托 ;
/// </summary>
/// <param name="act">第一个委托是你真的想要执行的这个方法</param>
/// <param name="callback">第二委托是当你执行完第一个方法之后, 想要执行的回调, 其实就是在一个线程内将两个方法并列执行了一次</param>
private void ThreadWithCallback(Action act, Action callback)
{
Thread thread = new Thread(() =>
{
act.Invoke();
callback.Invoke();
});
thread.Start();
}

 调用方法如下:

 private void btnThreads_Click(object sender, EventArgs e)
{ this.ThreadWithCallback(() => Console.WriteLine($"这里是action {Thread.CurrentThread.ManagedThreadId.ToString("00")}")
, () => Console.WriteLine($"这里是callback {Thread.CurrentThread.ManagedThreadId.ToString("00")}"));
}

 

b)         使用Thread完成带返回值的回调, 代码如下:

/// <summary>
/// 带返回值的异步调用; 带返回的异步调用 需要获取返回值 (会卡界面的)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
private Func<T> ThreadWithReturn<T>(Func<T> func)
{
#region 错误的写法 //T t ;
//Thread thread = new Thread(() =>
//{
// t = func.Invoke(); //这里还没有执行的时候, 它已经返回了
//});
//thread.Start();
// return t; #endregion T t = default(T);
Thread thread = new Thread(() =>
{ t = func.Invoke();
});
thread.Start();
return () =>
{
// while (thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(200); }
thread.Join();
return t;
};
}

 

调用方法如下:

private void withReturn_Click(object sender, EventArgs e)
{ Func<int> func = this.ThreadWithReturn<int>(() =>
{
Thread.Sleep(2000);
return DateTime.Now.Millisecond;
});
Console.WriteLine("上面是异步调用, 直接就会打印这句话, 这句话不会等待2秒"); int iResult = func.Invoke();
Console.WriteLine(iResult);
}

20181104_C#线程之Thread_ThreadPool_使用Thread实现回到和带参数的回调的更多相关文章

  1. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

  2. iOS多线程之1.从Thread看多线程的生命周期

      Thread 是多线程中最容易理解,但是使用起来又是最麻烦的一种多线程方法.为什么说容易理解呢?一个NSThread的对象就是一条线程.使用起来麻烦是因为,需要我们自己管理线程的生命周期:创建线程 ...

  3. Java多线程之this与Thread.currentThread()的区别——java多线程编程核心技术

      package mythread; public class CountOperate extends Thread{ public CountOperate(){ System.out.prin ...

  4. Java多线程之Runable与Thread

    Java多线程是Java开发中的基础内容,但是涉及到高并发就有很深的研究可做了. 最近看了下<Java并发实战>,发先有些地方,虽然可以理解,但是自己在应用中很难下手. 所以还是先回顾一下 ...

  5. Java基础-进程与线程之Thread类详解

    Java基础-进程与线程之Thread类详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程与线程的区别 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 ...

  6. iOS多线程之Thread

    多线程 • Thread 是苹果官方提供的,简单已用,可以直接操作线程对象.不过需要程序员自己管理线程的生命周期,主要是创建那部分 优缺点 面向对象,简单易用 直接操作线程对象 需要自己管理线程生命周 ...

  7. C# 多线程之Thread类

    使用System.Threading.Thread类可以创建和控制线程. 常用的构造函数有:   // 摘要: // 初始化 System.Threading.Thread 类的新实例,指定允许对象在 ...

  8. Java多线程之Thread、Runnable、Callable及线程池

    一.多线程 线程是指进程中的一个执行流程,一个进程中可以有多个线程.如java.exe进程中可以运行很多线程.进程是运行中的程序,是内存等资源的集合,线程是属于某个进程的,进程中的多个线程共享进程中的 ...

  9. python 线程之 threading(四)

    python 线程之 threading(三) http://www.cnblogs.com/someoneHan/p/6213100.html中对Event做了简单的介绍. 但是如果线程打算一遍一遍 ...

随机推荐

  1. java中的一些执行顺序,代码块,静态,构造,成员。。。。(转的)

    Java初始化顺序(转来的) 1在new B一个实例时首先要进行类的装载.(类只有在使用New调用创建的时候才会被java类装载器装入) 2,在装载类时,先装载父类A,再装载子类B3,装载父类A后,完 ...

  2. L156

    China has specified the definition and diagnosis standard for internet addiction in its latest adole ...

  3. reactNative 的一些学习

    手把手视频 学习资料大全 入门系列

  4. 我也说说Emacs吧(1) - Emacs和Vi我们都学

    好友幻神的<Emacs之魂>正在火热连载中,群里人起哄要给他捧捧场. 作为一个学习Emacs屡败屡战的用户,这个场还是值得捧一下的.至少我是买了HHKB键盘的... 从我的键盘说起 - 有 ...

  5. 为Java程序员金三银四精心挑选的五十道面试题与答案

    1.面向对象的特征有哪些方面? [基础] 答:面向对象的特征主要有以下几个方面: 1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问 ...

  6. Roslyn 入门:使用 Visual Studio 的语法可视化窗格查看和了解代码的语法树

    使用 Visual Studio 提供的 Syntax Visualizer,我们可以实时看到一个代码文件中的语法树.这对我们基于 Roslyn 编写静态分析和修改工具非常有帮助.本文将介绍如何安装它 ...

  7. 《DSP using MATLAB》示例Example7.7

    Type-4 Linear-Phase FIR filter 代码: h = [-4, 1, -1, -2, 5, 6, -6, -5, 2, 1, -1, 4]; M = length(h); n ...

  8. 在IIS上搭建FTP服务

    FTP服务 FTP是文件传输协议(File Transfer Protocol)的简称,该协议属于应用层协议(端口号通常为21),用于Internet上的双向文件传输(即文件的上传和下载).在网络上有 ...

  9. ballerina 学习九 Client endpoints

    说白了就是连接外部服务的,可以是http jms websocket .... 简单例子 代码 import ballerina/http; import ballerina/log; endpoin ...

  10. thinkphp 使每一个模板页都包括一个header文件和一个footer文件

    在开发的过程中,常常遇到要使每一个模板页都包括一个header文件和一个footer文件.thinkPHP的模板布局为我们提供了一个叫全局配置方式可以解决问题. 1. 在配置文件里开启LAYOUT_O ...