主要内容
- 进程和线程的概念
- 线程开启方式-异步委托
- 检测委托线程的结束(通过等待句柄和回调函数)
- 线程开启方式2-通过Thread类
- 线程的其它概念后台和前台线程,线程的优先级,线程的状态
- 线程开启方式3-线程池
- 线程开启方式4-任务
- 任务的其他知识
- 线程问题-争用条件和死锁
进程和线程的概念
进程和线程的一个简单解释(资料来源-阮一峰)
- 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
- 如果工厂的电力有限一次只能供给一个车间使用。也就是说一个车间开工的时候,其他车间就必须停工。背后的含义就是单个CPU一次只能运行一个任务(多核CPU可以运行多个任务)。
- 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
- 一个车间里可以有很多工人,他们协同完成一个任务。
- 线程就好比车间里的工人,一个进程可以包括多个线程。
- 车间的控件是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享空间。
- 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任意时刻,CPU总是运行一个进程,其他进程处于非运行状态。
- 一个防止他人进入的简单方法,就是门口加一把锁(厕所)。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写为Mutex)防止多个线程同时读写某一块内存区域。
- 还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
- 这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。
不难看出,Mutex是Semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为Mutex较为简单且效率高,所以在必须保证资源独占的情况下,可以采用这种设计。
操作系统的设计,因此可以归结为三点:
- 以多进程形式,允许多个任务同时运行;
- 以多线程形式,允许单个任务分成不同的部分运行;
- 提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。
线程开启方式-异步委托
有参有返回值线程
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
//一般我们会为比较耗时的操作,开启单独线程去执行
static int Test(int i, string str)
{
Console.WriteLine("Test" + i + str);
//让当前线程休眠(暂停线程的执行),ms
Thread.Sleep(100);
return 100;
}
//在Main线程中,语句的执行是从上到下的
static void Main(string[] args)
{
//通过委托开启一个线程
Func<int, string, int> a = Test;
//开启一个新线程去执行a所引用的方法
//IAsyncResult表示异步执行的状态
//可以取得当前线程的状态
IAsyncResult ar = a.BeginInvoke(66, "AAA", null, null);
//可以认为线程是同时执行的(异步执行)
Console.WriteLine("Main");
//如果当前线程没有执行完成
while (ar.IsCompleted == false)
{
Console.Write(".");
Thread.Sleep(10);
}
int res = a.EndInvoke(ar);
Console.WriteLine(res);
Console.ReadKey();
}
}
}
检测委托线程的结束(通过等待句柄和回调函数)
等待句柄
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
//一般我们会为比较耗时的操作,开启单独线程去执行
static int Test(int i, string str)
{
Console.WriteLine("Test" + i + str);
//让当前线程休眠(暂停线程的执行),ms
Thread.Sleep(100);
return 100;8
}
//在Main线程中,语句的执行是从上到下的
static void Main(string[] args)
{
//通过委托开启一个线程
Func<int, string, int> a = Test;
//开启一个新线程去执行a所引用的方法
//IAsyncResult表示异步执行的状态
//可以取得当前线程的状态
IAsyncResult ar = a.BeginInvoke(66, "AAA", null, null);
//可以认为线程是同时执行的(异步执行)
Console.WriteLine("Main");
//检测线程结束
//AsyncWaitHandle获取用于等待异步操作的完成
//WaitOne阻止当前线程,直到当前的System.Threading.WaitHandle 收到信号为止,
//同时使用 32 位带符号整数指定时间间隔
//并指定是否在等待之前退出同步域。
bool isEnd = ar.AsyncWaitHandle.WaitOne(1000);
if (isEnd)
{
int res = a.EndInvoke(ar);
Console.WriteLine(res);
}
Console.ReadKey();
}
}
}
回调函数
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
//一般我们会为比较耗时的操作,开启单独线程去执行
static int Test(int i, string str)
{
Console.WriteLine("Test" + i + str);
//让当前线程休眠(暂停线程的执行),ms
Thread.Sleep(100);
return 100;
}
//在Main线程中,语句的执行是从上到下的
static void Main(string[] args)
{
//通过回调 检测线程结束
Func<int, string, int> a = Test;
//倒数第二个参数是一个委托类型的参数,表示回调函数,就是当线程结束的时候会调用这个委托指向的方法
//倒数第一个参数用来给回调函数传递数据
IAsyncResult ar = a.BeginInvoke(66, "AAAA", OnCallBack, a);
Console.ReadKey();
}
static void OnCallBack(IAsyncResult ar)
{
Func<int, string, int> a = ar.AsyncState as Func<int, string, int>;
int res = a.EndInvoke(ar);
Console.WriteLine(res + "在回调函数中取得结果");
}
}
}
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
//一般我们会为比较耗时的操作,开启单独线程去执行
static int Test(int i, string str)
{
Console.WriteLine("Test" + i + str);
//让当前线程休眠(暂停线程的执行),ms
Thread.Sleep(100);
return 100;
}
//在Main线程中,语句的执行是从上到下的
static void Main(string[] args)
{
//通过回调 检测线程结束
Func<int, string, int> a = Test;
a.BeginInvoke(100, "siki", ar =>
{
int res = a.EndInvoke(ar);
Console.WriteLine(res + "在lambda表达式中取得");
}, null);
Console.ReadKey();
}
}
}
线程开启方式2-通过Thread类
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
//给Thread传递方法时,必须是object类型的
static void DownLoadFile(object fileName)
{
//可通过线程ID进行管理
Console.WriteLine("开始下载:" + Thread.CurrentThread.ManagedThreadId+ fileName);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
}
static void Main(string[] args)
{
/*
* 第一种方式(无参)
//创建Thread对象,这个线程并没有启动
Thread t = new Thread(DownLoadFile);
t.Start();//开始去执行线程
Console.WriteLine("Main");
Console.ReadKey();
*/
/*
* 第二种方式(无参)
Thread t = new Thread(() =>
{
Console.WriteLine("开始下载:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
});
t.Start();
Console.ReadKey();
} */
Thread t = new Thread(DownLoadFile);
t.Start("xxxx");//开始去执行线程
Console.ReadKey();
}
}
}
创建MyThread类(定义传递的数据)
using System;
using System.Threading;
namespace ConsoleApp1
{
class MyThread
{
private string filename;
private string filepath;
public MyThread(string fileName, string filePath)
{
filename = fileName;
filepath = filePath;
}
//提供一个线程方法,用于下载
public void DownFile()
{
Console.WriteLine("开始下载" + filename + filepath);
Thread.Sleep(2000);
Console.WriteLine("下载完成") ;
}
}
}
开启线程
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
MyThread my = new MyThread("xxx.bt ","http://www.xxx.com");
//构造Thread时候,可以传递一个静态方法,也可以传递一个动态普通方法
Thread t= new Thread(my.DownFile);
t.Start();
Console.ReadKey();
}
}
}
线程的其它概念后台和前台线程,线程的优先级,线程的状态
后台和前台线程
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
//给Thread传递方法时,必须是object类型的
static void DownLoadFile(object fileName)
{
//可通过线程ID进行管理
Console.WriteLine("开始下载:" + Thread.CurrentThread.ManagedThreadId + fileName);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
}
static void Main(string[] args)
{ //前台线程
//Thread t= new Thread(DownLoadFile);
//后台线程
Thread t = new Thread(DownLoadFile);
t.IsBackground = true;//设置为后台线程
t.Start("xxx");
}
}
}
线程的优先级
线程开启方式3-线程池
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void ThreadMethod(object state)
{
Console.WriteLine("线程开始:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
Console.WriteLine("线程结束:" + Thread.CurrentThread.ManagedThreadId);
}
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
ThreadPool.QueueUserWorkItem(ThreadMethod);
ThreadPool.QueueUserWorkItem(ThreadMethod);
ThreadPool.QueueUserWorkItem(ThreadMethod);
ThreadPool.QueueUserWorkItem(ThreadMethod);
ThreadPool.QueueUserWorkItem(ThreadMethod);
ThreadPool.QueueUserWorkItem(ThreadMethod);
Console.ReadKey();
}
}
}
线程开启方式4-任务
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void ThreadMethod()
{
Console.WriteLine("任务开始");
Thread.Sleep(2000);
Console.WriteLine("任务结束");
}
static void Main(string[] args)
{
//传递一个需要线程去执行的方法
//Task t = new Task(ThreadMethod);
//t.Start();
TaskFactory tf = new TaskFactory();
Task t = tf.StartNew(ThreadMethod);
Console.WriteLine("Main");
Console.ReadKey();
}
}
}
任务的其他知识
线程问题-争用条件和死锁
总结
学习siki的C#教程,打基础。
该套视频在泰课在线搜索《第三季 C#编程高级篇》,可以在线学习。