20181104_C#线程之Thread_ThreadPool_使用Thread实现回到和带参数的回调
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实现回到和带参数的回调的更多相关文章
- Java多线程之Runnable与Thread
Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...
- iOS多线程之1.从Thread看多线程的生命周期
Thread 是多线程中最容易理解,但是使用起来又是最麻烦的一种多线程方法.为什么说容易理解呢?一个NSThread的对象就是一条线程.使用起来麻烦是因为,需要我们自己管理线程的生命周期:创建线程 ...
- Java多线程之this与Thread.currentThread()的区别——java多线程编程核心技术
package mythread; public class CountOperate extends Thread{ public CountOperate(){ System.out.prin ...
- Java多线程之Runable与Thread
Java多线程是Java开发中的基础内容,但是涉及到高并发就有很深的研究可做了. 最近看了下<Java并发实战>,发先有些地方,虽然可以理解,但是自己在应用中很难下手. 所以还是先回顾一下 ...
- Java基础-进程与线程之Thread类详解
Java基础-进程与线程之Thread类详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程与线程的区别 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 ...
- iOS多线程之Thread
多线程 • Thread 是苹果官方提供的,简单已用,可以直接操作线程对象.不过需要程序员自己管理线程的生命周期,主要是创建那部分 优缺点 面向对象,简单易用 直接操作线程对象 需要自己管理线程生命周 ...
- C# 多线程之Thread类
使用System.Threading.Thread类可以创建和控制线程. 常用的构造函数有: // 摘要: // 初始化 System.Threading.Thread 类的新实例,指定允许对象在 ...
- Java多线程之Thread、Runnable、Callable及线程池
一.多线程 线程是指进程中的一个执行流程,一个进程中可以有多个线程.如java.exe进程中可以运行很多线程.进程是运行中的程序,是内存等资源的集合,线程是属于某个进程的,进程中的多个线程共享进程中的 ...
- python 线程之 threading(四)
python 线程之 threading(三) http://www.cnblogs.com/someoneHan/p/6213100.html中对Event做了简单的介绍. 但是如果线程打算一遍一遍 ...
随机推荐
- java中的一些执行顺序,代码块,静态,构造,成员。。。。(转的)
Java初始化顺序(转来的) 1在new B一个实例时首先要进行类的装载.(类只有在使用New调用创建的时候才会被java类装载器装入) 2,在装载类时,先装载父类A,再装载子类B3,装载父类A后,完 ...
- L156
China has specified the definition and diagnosis standard for internet addiction in its latest adole ...
- reactNative 的一些学习
手把手视频 学习资料大全 入门系列
- 我也说说Emacs吧(1) - Emacs和Vi我们都学
好友幻神的<Emacs之魂>正在火热连载中,群里人起哄要给他捧捧场. 作为一个学习Emacs屡败屡战的用户,这个场还是值得捧一下的.至少我是买了HHKB键盘的... 从我的键盘说起 - 有 ...
- 为Java程序员金三银四精心挑选的五十道面试题与答案
1.面向对象的特征有哪些方面? [基础] 答:面向对象的特征主要有以下几个方面: 1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问 ...
- Roslyn 入门:使用 Visual Studio 的语法可视化窗格查看和了解代码的语法树
使用 Visual Studio 提供的 Syntax Visualizer,我们可以实时看到一个代码文件中的语法树.这对我们基于 Roslyn 编写静态分析和修改工具非常有帮助.本文将介绍如何安装它 ...
- 《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 ...
- 在IIS上搭建FTP服务
FTP服务 FTP是文件传输协议(File Transfer Protocol)的简称,该协议属于应用层协议(端口号通常为21),用于Internet上的双向文件传输(即文件的上传和下载).在网络上有 ...
- ballerina 学习九 Client endpoints
说白了就是连接外部服务的,可以是http jms websocket .... 简单例子 代码 import ballerina/http; import ballerina/log; endpoin ...
- thinkphp 使每一个模板页都包括一个header文件和一个footer文件
在开发的过程中,常常遇到要使每一个模板页都包括一个header文件和一个footer文件.thinkPHP的模板布局为我们提供了一个叫全局配置方式可以解决问题. 1. 在配置文件里开启LAYOUT_O ...