Threads(线程)(二)
前一章我们提到了同步异步,多线程;在开始今天的文章之前,先来总结一下上一篇文章的内容,多线程的优点。
多线程有哪些优点呢,在这里通过代码依次来总结。
异步多线程的三大特点
1)同步方法卡界面,原因是主线程被占用;异步方法不卡界面,原因是计算交给了别的线程,主线程空闲
首先创建winfrom程序,建一个普通方法,然后异步、同步调用
/// <summary>
/// 执行动作:耗时而已
/// </summary>
private void TestThread(string threadName)
{
Console.WriteLine("TestThread Start Name={2}当前线程的id:{0},当前时间为{1},", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), threadName);
long sum = 0;
for (int i = 1; i < 999999999; i++)
{
sum += i;
}
//Thread.Sleep(1000);
Console.WriteLine("TestThread End Name={2}当前线程的id:{0},当前时间为{1},计算结果{3}", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), threadName, sum);
}
同步调用
private void btnSync_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine();
Console.WriteLine("**********************btnSync_Click Start 主线程id {0}****************************************", Threa d.CurrentThread.ManagedThreadId); for (int i = 0; i < 5; i++)
{
string name = string.Format("btnSync_Click_{0}", i);
TestThread(name);
} watch.Stop();
Console.WriteLine("**********************btnSync_Click End 主线程id {0} {1}*******************************", Thread.Cur rentThread.ManagedThreadId, watch.ElapsedMilliseconds);
Console.WriteLine();
}
异步调用
private void btnAsync_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine();
Console.WriteLine("***********************btnAsync_Click Start 主线程id {0}**********************************", Thread.CurrentThread.ManagedThreadId); for (int i = 0; i < 5; i++)
{
string name = string.Format("btnAsync_Click_{0}", i);
Action act = () => this.TestThread(name);
act.BeginInvoke(null, null);
} watch.Stop();
Console.WriteLine("**********************btnAsync_Click End 主线程id {0} {1}************************************", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
Console.WriteLine();
}
通过运行,你会发现当同步调用时,整个winfrom界面是不能动的,你一动程序运行就会停止。
这是因为当使用同步方法时,主线程在运算,而移动winform界面的动作也是需要主线程操作。这就会形成卡顿或动不了的情况。
而异步调用就不会出现这种情况。上一章也讲过,异步调用时,方法体的运算实在其他线程中运行,而主线程会继续往下运行,很快就结束了运算,这个时候主线程是空闲的,所以就不会出现这总情况。
2) 同步方法慢,原因是只有一个线程计算;异步方法快,原因是多个线程同时计算,但是更消耗资源,不宜太多
通过运行结果也可以分析,同步方法主线程运行时间有十几秒,而异步方法也就六七秒。通过对CPU运行分析,就更能够理解了。
当不运行是,我么的CPU利用率保持在20%-30%之间。
但我们调用同步方法时,利用率到达了50%-60%之间
当我们调用异步方法时,CPU爆表,达到100%
所以说异步调用不能在程序中无限使用,不知道在哪里看过,据不官方的报道,程序的线程数在电脑cpu核数4倍左右会达到最佳。
简单来说异步多线程就是资源来换取时间。
3)异步多线程是无序的,启动顺序不确定、执行时间不确定、结束时间不确定
通过分析前面的运行结果可以看出,线程开始运行顺序是10、12、13、11、14,而结束是12、10、13、11、14。而且方法体运行时按理说应该是在循环体中从0-4依次开始,而异步开始的顺序是0、2、1、3、4,这些都是不确定的。
*************************************************************************************************************************************************************************************
前面总结了这么多,进入今天的主题,Thread(线程)。
其实线程(Thread)和前面的异步调用差不太多,关于概念的东西在前一篇文章中有阐述,这里就不说明了。那么我们怎么声明线程呢?
private void btnThread_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine();
Console.WriteLine("***********************btnThread_Click Start 主线程id {0}**********************************", Thread.CurrentThread.ManagedThreadId); List<Thread> threadList = new List<Thread>();
for (int i = 0; i < 5; i++)
{
string name = string.Format("btnThread_Click_{0}", i);
ThreadStart method = () => this.TestThread(name);
Thread thread = new Thread(method); thread.Start(); } watch.Stop();
Console.WriteLine("**********************btnThread_Click End 主线程id {0} {1}************************************", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
Console.WriteLine();
}
首先。就跟我们声明一个委托一样声明一个ThreadStart对象,然后把对象当变量的形式添加到线程中,最后开始线程。
注意:这样开始线程 默认前台线程,就是说程序退出后,计算任务会继续进行。
那么怎么改为后台线程呢,一般来说程序都退出了,就不需要进行计算任务了。这个时候就需要加一个属性
thread.IsBackground = true;// 后台线程:程序退出,计算立即结束
如果我们需要主线程等到其他线程结束后在结束,应该怎么做呢,也就是说希望主线程可以等待一段时间。
private void btnThread_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine();
Console.WriteLine("***********************btnThread_Click Start 主线程id {0}**********************************", Thread.CurrentThread.ManagedThreadId); List<Thread> threadList = new List<Thread>();
for (int i = 0; i < 5; i++)
{
string name = string.Format("btnThread_Click_{0}", i);
ThreadStart method = () => this.TestThread(name);
Thread thread = new Thread(method);//1 默认前台线程:程序退出后,计算任务会继续
thread.IsBackground = true;//2 后台线程:程序退出,计算立即结束
thread.Start();
threadList.Add(thread);
} foreach (Thread thread in threadList)
{
thread.Join();
} watch.Stop();
Console.WriteLine("**********************btnThread_Click End 主线程id {0} {1}************************************", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
Console.WriteLine();
}
我们需要声明一个Thread类型的List。没开始一个线程,就把线程加到list中,然后遍历,把每个线程Join(),因为这一句话是主线程运行的,也就是说主线程在等待其他线程结束后再接着运行。
Thread(线程)利于线程的回收,有点浪费资源,后来框架推出了池的概念。
也就是ThreadPool。就相当于添加了一个管理员,把线程都放在池子里,便于线程的管理。
private void btnThreadPool_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine();
Console.WriteLine("***********************btnThreadPool_Click Start 主线程id {0}**********************************", Thread.CurrentThread.ManagedThreadId); for (int i = 0; i < 5; i++)
{
string name = string.Format("btnThreadPool_Click_{0}", i);
WaitCallback method = t => this.TestThread(t.ToString());
ThreadPool.QueueUserWorkItem(method, name);
} watch.Stop();
Console.WriteLine("**********************btnThreadPool_Click End 主线程id {0} {1}************************************", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
Console.WriteLine();
}
我们只需要ThreadPool.QueueUserWorkItem(),接受一个WaitCallback类型的变量,和一个参数,这个参数会作用于WaitCallback。这就完成了线程的使用,简单了许多。。。但是如果我们也需要线程等待呢,该怎么做。
private void btnThreadPool_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine();
Console.WriteLine("***********************btnThreadPool_Click Start 主线程id {0}**********************************", Thread.CurrentThread.ManagedThreadId); ManualResetEvent mre = new ManualResetEvent(false);
WaitCallback method = t =>
{
this.TestThread(t.ToString());
mre.Set();//打开
};
ThreadPool.QueueUserWorkItem(method, "TestManualResetEvent"); Console.WriteLine("我们来干点别的。。。。");
Console.WriteLine("我们来干点别的。。。。");
Console.WriteLine("我们来干点别的。。。。");
Console.WriteLine("我们来干点别的。。。。");
mre.WaitOne(); watch.Stop();
Console.WriteLine("**********************btnThreadPool_Click End 主线程id {0} {1}************************************", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
Console.WriteLine();
}
只需要声明ManualResetEvent 并且设置为false;然后在委托中set打开一下,就行了。相应的ReSet就是关闭。关闭后如果不在打开就不会执行后面的内容
ManualResetEvent mre = new ManualResetEvent(false);
new Action(() =>
{
Thread.Sleep(5000);
Console.WriteLine("委托的异步调用");
mre.Set();//打开
}).BeginInvoke(null, null); mre.WaitOne();
Console.WriteLine("12345");
mre.Reset();//关闭
new Action(() =>
{
Thread.Sleep(5000);
Console.WriteLine("委托的异步调用2");
// mre.Set();//打开
}).BeginInvoke(null, null);
mre.WaitOne();
Console.WriteLine("23456");
后面的打印23456就会停止打印,会一直处于等待状态。
******************************************************************************************************************************************************************************************************************************
后来在.net3.0时代推出了task概念。task,在这里讲几种创建方式,这里只是稍微涉及一下,在后面的章节中会细讲,
1)通过TaskFactory ,直接StartNew开始并创建一个线程。
private void btnTask_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine();
Console.WriteLine("***********************btnTask_Click Start 主线程id {0}**********************************", Thread.CurrentThread.ManagedThreadId); TaskFactory taskFactory = new TaskFactory(); for (int i = 0; i < 5; i++)
{
string name = string.Format("btnAsync_Click_{0}", i);
Action act = () => this.TestThread(name);
taskFactory.StartNew(act);
} watch.Stop();
Console.WriteLine("**********************btnTask_Click End 主线程id {0} {1}************************************", Thread.CurrentThread.ManagedThreadId, watch.ElapsedMilliseconds);
Console.WriteLine();
}
2)建Task 对象,然后Start。
Task task = new Task(act);
task.Start();
3)直接对Task对象Run(),这样也可以对线程创建并开始;
Task task = Task.Run(act);
当然Task的创建不知这么几种,这里只是稍微涉及,在后面的章节会细讲^-^
未完待续。。。。。。。。。。
Threads(线程)(二)的更多相关文章
- 【Qt开发】事件循环与线程 二
事件循环与线程 二 Qt 线程类 Qt对线程的支持已经有很多年了(发布于2000年九月22日的Qt2.2引入了QThread类),Qt 4.0版本的release则对其所有所支持平台默认地是对多线程支 ...
- C#中的线程二(Cotrol.BeginInvoke和Control.Invoke)
C#中的线程二(Cotrol.BeginInvoke和Control.Invoke) 原文地址:http://www.cnblogs.com/whssunboy/archive/2007/06/07/ ...
- python并发编程之线程(二):死锁和递归锁&信号量&定时器&线程queue&事件evevt
一 死锁现象与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将 ...
- C#中的线程(二) 线程同步基础
1.同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具: 简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程 ...
- C#中的线程二(BeginInvoke和Invoke)
近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资料,整理如下.感谢这篇文章对我的理解Invoke和BeginInvoke的真正含义 . (一)Control的In ...
- 【转】Qt事件循环与线程 二
转自:http://blog.csdn.net/changsheng230/article/details/6153449 续上文:http://blog.csdn.net/changsheng230 ...
- 2.Perl 多线程:Threads(线程返回值)
use warnings; use strict; use threads; sub TEST{ print "Hello, World!\n"; 'a'/); } #返回列表方法 ...
- java 线程二
一.线程的优先级别 线程优先级别的使用范例: 1 package cn.galc.test; 2 3 public class TestThread6 { 4 public static void m ...
- Python线程二
转自:https://www.cnblogs.com/chengd/articles/7770898.html 1. threading.Lock() import threading balance ...
- java基础学习总结——线程(二)
一.线程的优先级别
随机推荐
- ReactNative学习之Html基础
前言: React Native开发作为一种新型的移动开发方式,个人觉得App的一部分需求会逐步替换成这种方式,也是公司移动开发人员所必须掌握的一种开发技术,所以鉴于这种情况我觉得很有必要学习一下,特 ...
- .net 利用Emit将object转为DbParameter,DataTable转为List<>
先放测试结果图,测试的方法是拷贝了老赵的一个简单的性能计数器:CodeTimer.发现速度还是比利用反射来获取快了2倍左右的,将object转为DbParameter的反射方法我没写. ...
- iOS 手势
一.看这里 二.抽象类 UIGestureRecognizer 继承于该类的有7类:轻点,捏合,拖拽,滑动,长按,旋转,边缘滑动; 一个手势可以绑定多个事件 - (void)addTarget:( ...
- ap.net core 教程(三) - 新建项目
ASP.NET Core - 新建项目 在这一章,我们将讨论如何在Visual Studio中创建一个新项目. 只要你安装了Visual Studio 2015的.net core工具,您就可以开始构 ...
- 解决jmeter请求不成功或者报403错误
有同学遇到这种情况,jmeter请求一个网站,各项参数填写正确,可是响应是403,同样的请求放在浏览器执行就没有问题: 这是因为被请求的网站做了请求来源过滤,来源不明的请求拒绝访问,我们需要在jmet ...
- 没有main方法真的不能执行代码了吗?
今天看北大慕课遇到一段代码,于是下载下来跑了一下,奇葩的是,没有main方法既没报错,还出了结果. 下面贴出代码: class InitialTest { public static void mai ...
- [0] Visual studio 2010 快捷键大全
[窗口快捷键]Ctrl+W,W: 浏览器窗口 Ctrl+W,S: 解决方案管理器 Ctrl+W,C: 类视图 Ctrl+W,E: 错误列表 Ctrl+W,O: 输出视图 trl+W,P: 属性窗口 C ...
- Java IO学习笔记二
Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...
- 如何在linux如何安装nginx服务器
1.进入命令窗口: 2.进入nginx.org 网站 下载安装包nginx-1.8.0.tar.gz 3.进入下载的文件夹 4.加压下载的压缩包 tar -zxvf nginx-1.8.0.tar. ...
- ecshop屏蔽wap功能
用手机打开ecshop网店,就会被重定向到mobile文件夹,如果打开wap功能,就能看到wap版的网站.但现在智能手机横行,iphone.安卓可以跟电脑一样浏览和购物,这个wap功能就有点鸡肋.现在 ...