一、谈进程和线程。
进程:当一个程序被打开运行时,此刻它就是一个进程。它是操作系统进行资源调度和分配的基本单位,一个进程可以有若干个线程,线程也可以同时帮进程做多个事情
线程:线程程序执行流的最小单位,自己不拥有资源,但是他可以与同进程中的其他线程共享进程中所有的资源,一个线程可以创建或者终止其他的线程
多线程:在单CPU系统的单位时间内,CPU只能允许单个线程,顺序取决于线程的级别{从低到高:Lowest、BelowNormal、Normal(默认)、AboveBoral、Highest},只因为相互切换频密且时间非常短暂,所以说为多线程可以视为同时运行。
二、用Thread类创建线程
1、几个经常用到属性:
IsAlive:bool类型,用于判断线程是否被执行,创建线程后不执行该值为False,调用Start()方法运行线程后该值为True,当线程结束后该值为False,其余ThreadState状态都为True
IsBackground:是否为前台线程,用Thread类创建线程(Thread thread=new Thread())默认为前台线程,即IsBackground=False,CLR线程池所建立的线程总是默认为后台线程。当主程序关闭时前台线程不会随主程序的关闭而关闭
ManagedThreadId:获取线程唯一标识符
Name:线程名字
Priority:线程有限级别
ThreadState:线程状态{unstarted、Running、WaitSleepJoin等等}
2、熟悉两个委托:public delagate void ThreadStart()、public delegate void ParameterizedThreadStart(object a)
通过以上应该很清楚的看到这两个委托的使用方法。
3、线程中的一些方法:
Sleep:把正在运行的线程挂起一段时间
Abort:终止本线程
Join:阻塞调用线程,只当这个线程完成后则继续之后的操作或线程
Interrupt:中断处于WautSleepJoin状态的线程
Start:执行本线程
Suppend表示挂起,Resume表示恢复挂起的线程(已不能用,这里不做解释)
三、线程池
ThreadPool线程池中包含了QueueUserWorkItem这个方法用来进行异步调用,可以带Object
的参数,也可以不带,不带参数默认为NULL
ThreadPool.SetMaxThreads()用于设置线程池最大线程数量,有两个参数,第一个设置工作者线程,第一个用于设置IO线程
ThreadPool.QueueUserWorkItem(new WaitCallBack(方法名称),参数)
上面这一步就已经在线程池中创建了一个线程,WaitCallBack同样是一个委托: public delegate void WaitCallback(object state);
通过QueueUserWorkItem的方式来启动线程比较简单,但是不满足我们在实际开发中的需要,它只能最多带一个参数,而且无返回值。
四、使用委托类来开始一个线程
1、委托类几个重要的方法:
Invoke();调用Invoke()方法时,对应此委托的所有方法都会被执行
BeginInvoke(),EndInvoke()支持委托的异步调用,由BeginInvoke启动的线程都属于线程池中的线程‘
2、BeginInvoke参数说明:
倒数第一个参数用于调用外部的数据,相当于网回调函数中传递参数
倒数第二个参数是回调函数
前面参数为匹配委托的方法中的参数
public delegate string GetString(string a)//声明一个委托
static void Main(string[] arg)
{
GetString getstring=new GetString(GetName);//建立一个委托,并为他绑定一个方法
//异步调用委托需要用到IAsyncResult这一个接口
【//这种方式异步调用需要等到异步调用的方法执行完毕后才会调用主线程,不符合实际开发需要
IAsyncResult Result=getstring.BeginInvoke("张三",null,null);
string name=getstring.EndInvoke(Result);//获取方法GetName返回来的值
Console.WriteLine(name);
Console.WriteLine("主线程");
}
】
【//通过IAsyncResult中的成员IsCompleted来获取异步操作是否完成,bool类型,当异步调用操作未完成也会继续执行主线程
IAsyncResult Result=getstring.BeginInvoke("张三",null,null);
while(!Result.IsCompleted)
{
Console.WriteLine("主线程");
}
string name=getstring.EndInvoke(Result);//获取方法GetName返回来的值
Console.WriteLine(name);
】
【//使用WailHandle中的WaitOne()这个方法完成异步调用
IAsyncResult Result=getstring.BeginInvoke("张三",null,null);
while(!Result.AsyncWaitHandle.WaitOne(200))//参数代表时间,以一种轮询的方式去判断异步调用是否完成
{
Console.WriteLine("主线程");
}
string name=getstring.EndInvoke(Result);//获取方法GetName返回来的值
Console.WriteLine(name);
】
【//如果需要监视多个异步调用是否完成,这个时候我们需要声明一盒WaitHandle的数组,将需要监视的对象传进去
IAsyncResult Result=getstring.BeginInvoke("张三",null,null);
Waithandle[] waitlist=new Waithandle[]{Result.AsyncWaitHandle,.....};
//主要有两个方法进行监视 1、WaitAll(数组,时间),等待所有异步调用完成后返回一个bool值,2、WaitAny
等待waitlist其中一个完成异步调用后即返回一个int类型的值,该值就是数组的索引
while(!WaitHandle.WaitAll(waitlist,200))
{
Console.WriteLine("主线程");
}
】
pubslic string GetName(string name)
{
return "Hi"+name;
}
五、利用Parallel实现并行《也是一种实现线程的方式》
说说Palaller之中的三个常用的方法:Invoke,For,ForEach
1、Paraller.Invoke:这是最简单的的将串行的代码并行化,里面可以装好几个方法
例如:public void A(){Console.WriteLine("A")}
public void B(){Console.WrteLine("B")}
public void C()
{
Parallel.Invoke(A,B);//相当于两个线程,一个执行A方法,一个执行B方法
}
用Invoke需要注意他们里面执行的方法没有返回值,也不带参数
2、Paraller.For:这个类似于for循环,写法如下
Paraller.For(0,1,Item=>
{
A();
B();
})
需要注意的是用Paraller.For可以执行带返回值和带参数的方法
3、Paraller.ForEach这个方法同样类似于foreach,写法如下:
//假设声明一个集合
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
Paraller.Foreach(list,item=>
{
A();
B();
})
//同样使用Parallel.Foreach是可以执行带返回值和带参数的方法
六、Task实现多线程
上面主要讲了Task的几种创建方式,还有一些需要用到的方法,在这里具体说明一下:
1、Task创建方式有两种,一种是可以直接去执行不用Start,另外一种需要执行Start方法才可以执行线程,获取线程的状态主要用:Task.Status()这个方法来获取
2、Task里面有一个等待机制,这个可以是我们能够更好的操作多线程应用
Task.Wait() 方法说明:当用这个方法时,程序会等待这个线程执行完成,才会执行下面的语句
Task.WaitAll():里面参数带你需要管理的几Task线程,当里里面的线程执行完毕后才会执行下面的语句
Task.WaitAny:里面参数带你需要管理的几Task线程,里面只要有一个线程执行完毕就会执行下面的语句
3、适当的用ContinueWith,这个方法适用于当你的线程执行完毕后会继续执行下面的方法
例如代码中:var tb=t2.ContinueWith<String>(task=>
{
return "";
})
当t2线程执行完毕后会继续执行后面的return "";当然此处也可以些其他的方法,String 只是一个返回值的类型
我们可以通过 string a=tb.Result;来获取返回值
五、Task嵌套
上面例子就是一个简单的Task嵌套,需要注意的是:
TakCreateOptions.AttschedParent这个是什么意思呢,是将父亲和儿子的关系连在一起,也就是说当用到TakCreateOptions.AttschedParent时,
线程P和线程P1是一个整体,此时用P.Wait()这个方法则表示当P1和P两个线程都执行完毕后才会执行后面的代码。不使用,结果则相反。
再举一个例子,这个例子参照别的博客:
这个嵌套的意思是