C# 多线程详解 Part.01(UI 线程、子线程)
基本概念
什么是进程?
当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。一个进程至少有一个主线程。
什么是线程?
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
什么是多线程?
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
多线程的好处?
可以提高 CPU 的利用率。在多线程程序中,一个线程必须等待的时候,CPU 可以运行其它的线程而不是等待,这样就大大提高了程序的效率。
多线程的不利方面?
线程也是程序,所以线程需要占用内存,线程越多占用内存也越多。
多线程需要协调和管理,所以 CPU 需要花时间来跟踪线程。
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。
线程太多会导致控制太复杂,最终可能造成很多 Bug。
static void Main(string[] args)
{
Thread.CurrentThread.Name = "It's Main Thread";
Console.WriteLine(Thread.CurrentThread.Name + " [Status:" + Thread.CurrentThread.ThreadState + "]");
}
通过 Thread 类的静态属性 CurrentThread 可以获取当前正在执行的线程。不管创建了多少个这个类的实例,但是类的静态属性在内存中只有一个。很容易理解 CurrentThread 为什么是静态的--虽然有多个线程同时存在,但是在某一个时刻,CPU 只能执行其中一个!
在.net framework class library 中,所有与多线程机制应用相关的类都是放在 System.Threading 命名空间中。
Thread 类有几个至关重要的方法:
Start() | 启动线程 |
Sleep(int) | 静态方法,暂停当前线程指定的毫秒数 |
Abort() | 通常使用该方法来终止一个线程 |
Suspend() | 该方法并不终止未完成的线程,它仅仅挂起线程,以后还可恢复 |
Resume() | 恢复被 Suspend()方法挂起的线程的执行 |
操纵一个线程
static void Main(string[] args)
{
Console.WriteLine("Thread Start/Stop/Join Sample:");
// 创建一个线程,使之执行 Beta 方法
Thread oThread = new Thread(Beta);
// 实际上,Start 方法只是通知 CPU 此线程可以被执行,但具体执行时机则由 CPU 自行决定。
oThread.Start();
while (!oThread.IsAlive)
{
Thread.Sleep(1);
}
oThread.Abort();
oThread.Join();
Console.WriteLine();
Console.WriteLine("Beta has finished");
try
{
Console.WriteLine("Try to restart the Alpha.Beta thread");
oThread.Start();
}
catch (ThreadStateException)
{
Console.WriteLine("ThreadStateException trying to restart Alpha.Beta. ");
Console.WriteLine("Expected since aborted threads cannot be restarted.");
Console.ReadLine();
}
}
public static void Beta()
{
while (true)
{
Console.WriteLine("Beta is running in its own thread.");
}
}
试图用 Thread.Start() 方法重新启动线程 oThread,但显然 Abort() 方法带来的后果是不可恢复的终止线程,所以最后程序会抛出 ThreadStateException 异常。
线程的优先级
当线程之间争夺 CPU 时,CPU 按照线程的优先级给予服务。在 C# 应用程序中,用户可以设定 5 个不同的优先级,由高到低分别是 Highest,AboveNormal,Normal,BelowNormal,Lowest,在创建线程时如果不指定优先级,那么系统默认为 ThreadPriority.Normal。
通过设定线程的优先级,我们可以安排一些相对重要的线程优先执行,例如对用户的响应等等。
static void Main(string[] args)
{
Thread t1 = new Thread(() =>
{
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
for (int i = 0; i < 20000; i++)
{
// 模拟耗时工作
var obj = new { name = "XXX", age = 37 };
GC.Collect();
}
watch.Stop();
Console.WriteLine("t1 finished[ {0} ]", watch.ElapsedMilliseconds);
});
Thread t2 = new Thread(() =>
{
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
for (int i = 0; i < 20000; i++)
{
var obj = new { name = "XXX", age = 37 };
GC.Collect();
}
watch.Stop();
Console.WriteLine("t2 finished[ {0} ]", watch.ElapsedMilliseconds);
});
t1.Priority = ThreadPriority.AboveNormal;
t2.Priority = ThreadPriority.BelowNormal;
t1.Start();
t2.Start();
}
线程的调度算法非常复杂。记住,优先级高的线程并不一定先执行,但 CPU 会将更多的时间片分给优先级高的线程,因此,在相同任务量的前提下,高优先级线程将会较快的完成任务。
Winform 中多线程的应用
在 Winform 程序总,一般称绘制窗体和响应用户的线程为主线程,或 UI 线程。单线程最显著的缺点是,当一个事件发生,程序进行一个耗时的运算动作时,UI 线程会出现假死现象,此时会无视对用户的响应。
下面的代码会模拟一些不同的情况:
void DoSomething()
{
for (int i = 0; i < 900000000; i++)
{
}
MessageBox.Show("It's finished.");
}
void ShowStr(object obj)
{
var list = obj as List<string>;
if (list != null)
{
foreach (var item in list)
{
MessageBox.Show(item.ToString());
}
}
else
MessageBox.Show("null");
}
// UI 单线程,运行时窗体会卡死一段时间
private void btnUI_Click(object sender, EventArgs e)
{
DoSomething();
}
// 调用无參函数,此时窗体能响应用户
private void btnThreadA_Click(object sender, EventArgs e)
{
Thread thread = new Thread(DoSomething);
thread.Start();
}
// 当所有前台线程都关闭时,后台线程将立即结束运行,无条件的关闭
// 而前台线程运行时,即使关闭 Form 主程序,该线程仍将继续运行,直到计算完毕
private void btnThreadB_Click(object sender, EventArgs e)
{
Thread thread = new Thread(DoSomething);
thread.IsBackground = true;
thread.Start();
}
// 调用有参函数
private void btnThreadC_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ShowStr);
thread.Start(new List<string> { "Jacky", "Skysoot", "Sam" });
}
要注意的是,线程在调用有参函数时,通过 Start() 方法传递了参数给指定委托,该委托又将参数传递给了该线程欲运行的函数。看微软 Thread 类定义的元数据:
Thread 类的 4 个构造函数基本分为 2 类,有参和无參。而 ParameterizedThreadStart 委托定义的方法原型的参数为 Object 类型,这提高了传参最大的灵活性。当然,在被调用的函数内部,需要依据一定的约定将 Object 对象进行转型处理。
绕圈子封装一个线程类进行函数和参数的传递
// 可定义各类型委托 示例暂定一个
public delegate void Do(object obj);
public class Worker
{
Do method;
object obj;
private void Work()
{
method(obj);
}
// 创建工人线程时 new 出工人实例 并在线程上指定 Work()
public static Thread CreateWorkerThread(Do method, object obj)
{
Worker worker = new Worker();
worker.method = method;
worker.obj = obj;
Thread t = new Thread(worker.Work);
return t;
}
}
// 任务类
public class Quest
{
public static void Quest1(object obj)
{
Console.WriteLine("工人开始:" + obj.ToString() + "\r\n");
}
public static void Quest2(object obj)
{
string[] list = obj as string[];
if (obj != null)
{
foreach (var item in list)
{
Console.WriteLine("工人开始:" + item);
}
}
}
}
public class Test
{
public static void Main(string[] args)
{
Thread t1 = Worker.CreateWorkerThread(Quest.Quest1, "搬砖");
t1.Start();
Thread t2 = Worker.CreateWorkerThread(Quest.Quest2, new string[] {"唱歌", "跳舞", "打台球" });
t2.Start();
}
}
这种封装只是一种启发的方式而已,并非模式。但封装委托后的好处在于,调用方可以灵活指定 Worker 类执行什么类型的任务,加工什么参数,而无需再去考虑其余事情。
C# 多线程详解 Part.01(UI 线程、子线程)的更多相关文章
- android handle详解2 主线程给子线程发送消息
按照android handler详解分析的原理我们可以知道,在主线程中创建handle对象的时候,主线程默认创建了一个loop对象使用threalocal函数将loop对象和主线程绑定. 我们能不能 ...
- iOS开发——多线程OC篇&多线程详解
多线程详解 前面介绍了多线程的各种方式及其使用,这里补一点关于多线程的概念及相关技巧与使用,相信前面不懂的地方看了这里之后你就对多线程基本上没有什么问题了! 1——首先ios开发多线程中必须了解的概念 ...
- iOS开发——GCD多线程详解
GCD多线程详解 1. 什么是GCD Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,简单来说,GCD就是iOS一套解决多线程的机制,使用GCD能够最大限度简化多线程 ...
- Java 多线程详解(四)------生产者和消费者
Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...
- java中多线程详解-synchronized
一.介绍 当多个线程涉及到共享数据的时候,就会设计到线程安全的问题.非线程安全其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”.发生脏读,就是取到的数据已经被其他的线 ...
- python多线程详解
目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 二.线程实现 threading模块 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 互斥锁 递归锁 信号量 ...
- C#多线程详解(一) Thread.Join()的详解
bicabo C#多线程详解(一) Thread.Join()的详解 什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程 ...
- [Python 多线程] 详解daemon属性值None,False,True的区别 (五)
本文以多个例子介绍Python多线程中daemon属性值的区别. 回顾: 前面的文章简单介绍了在现代操作系统中,每一个进程都认为自己独占所有的计算机资源. 或者说线程就是独立的王国,进程间是相对独立的 ...
- 关于Activity销毁,而绘制UI的子线程未销毁出现的问题
项目总结 ----------------------------------------------------------------------------------------------- ...
随机推荐
- js中对radio和checkbox是否选中的判断
一.js判断checkbox 例如:<div class="checkbox" style="width: 150px;"> <label&g ...
- python的class的__str__()和__repr__()函数
repr(object) 返回一个可以用来表示对象的可打印字符串首先,尝试生成这样一个字符串,将其传给 eval()可重新生成同样的对象 否则,生成用尖括号包住的字符串,包含类型名和额外的信息(比如地 ...
- vs 2005 使用 boost regex
第一步: Boost 入门及其VS2005下编译boost库 boost.regex库安装指南 深入浅出之正则表达式(一) C++中三种正则表达式比较(C regex,C ++regex,boo ...
- FACADE
1 意图:为子系统中的一组接口提供一个一直的界面,Facade模式定义了一个高层接口.这个接口使得这一子系统更加容易使用, 2 动机,便于不需要更多功能的人通过Facade 简化使用 3 适用性: . ...
- .net 连接数据库
"@"符号是防止将后面字符串中的"\"解析为转义字符. using System.Data; using System.Data.SqlClient; ... ...
- SSH框架的配置
^_^阅读本文前请先浏览 : http://www.cnblogs.com/LiJinfu/p/5842890.html 步骤 : 一.编写web.xml配置文件 该文件路径在项目文件下的WebCon ...
- 补psp进度(11月4号-9号)
这周psp进度 11月4号 内容 开始时间 结束时间 打断时间 净时间 小伙伴聊天实现 9:45 10:49 0 64m 学习HttpURLConnection 14:13 15:48 10m 85m ...
- csu 1804 有向无环图
题目地址 分析:从复杂度来看,一定不可能是枚举和来计算.1e5的规模来看,应该是复杂度比较合适. 我是这么想的,对于三个点,假设1->2有a种走法,2->3有b种走法.那么1->3应 ...
- SSM框架学习之高并发秒杀业务--笔记1-- 项目的创建和依赖
在慕课网上看了Java高并发秒杀API视屏后,觉得这个案例真的让我学到了很多,现在重新自己实现一遍,博客记下,顺便分析其中的要点. 第一步是项目的创建和依赖 利用Maven去创建工程然后导入Idea中 ...
- Tomcat下安装solr6.x
1.官网上下载solr(http://lucene.apache.org/solr/)和tomcat(http://tomcat.apache.org/) 测试用到的版本:solr-6.2.1.apa ...