```C#学习(五)···

来源:互联网 发布:超星泛雅网络课程 编辑:程序博客网 时间:2024/06/05 08:25

进程和线程
一般情况下,一个应用程序有一个进程,一个进程下有好多线程。
一个线程里面的语句是从上到下执行的
线程与线程是同时工作的,可以视为异步运行
1,计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
2,如果工厂的电力有限一次只能供给一个车间使用。也就是说一个车间开工的时候,其他车间就必须停工。背后的含义就是。单个CPU一次只能运行一个任务。(多核CPU可以运行多个任务)
3,进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
4,一个车间里,可以有很多工人,他们协同完成一个任务。
5,线程就好比车间里的工人。一个进程可以包括多个线程。
6,车间的控件是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享空间。
7,一个防止他人进入的简单方法,就是门口加一把锁(厕所)。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫”互斥锁”(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
8,还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
9,这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做”信号量”(Semaphore),用来保证多个线程不会互相冲突。
不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。
10,操作系统的设计,因此可以归结为三点:
(1)以多进程形式,允许多个任务同时运行;
(2)以多线程形式,允许单个任务分成不同的部分运行;
(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

资料来源-阮一峰

委托方式发起线程

static void Test(int i,String str){Console.WriteLine("test");}static void Main(){Action<int,string> a = Test;a.Begininvoke(100,"a",null,null);//开启一个新的线程去执行a所引用的方法}static int Test(int i,String str){Thread.Sleep(100);让当前线程暂停,单位msConsole.WriteLine("test");}static void Main(){Func<int,string,int> a = Test;IAsyncResult ar=a.Begininvoke(100,"a"100,null,null);//开启一个新的线程去执行a所引用的方法,返回值为IAsyncResult可以取得当前线程的状态ar.IsCompleted 判断当前线程是否执行完毕a.EndInvoke(ar);//取得异步线程的返回值}

我们会为比较耗时的操作开启单独线程去执行

监测委托线程的结束

通过等待句柄AsyncWaitHandle

static int Test(int i,String str){Thread.Sleep(100);让当前线程暂停,单位msConsole.WriteLine("test");}static void Main(){Func<int,string,int> a = Test;IAsyncResult ar=a.Begininvoke(100,"a"100,null,null);//开启一个新的线程去执行a所引用的方法,返回值为IAsyncResult可以取得当前线程的状态//通过回调,监测线程结束bool isEnd = ar.AsyncWaitHandle.WaitOne(1000);//1000毫秒表示超时时间,如果等待了1000毫秒线程还没有结束,那么会返回false,如果结束了会返回true}

BeginInvoke倒数第二个参数是一个委托类型的回调函数需要有一个IAsyncResult类型的参数
倒数第一个参数是用来个回调函数传递数据

通过Thread类创建线程

static void DownloadFile(){Thread.Sleep(2000);Console.WriteLine("a");int id = Thread.CurrentThread.ManagedThreadId;}static void Main(string[] args){Thread t = new Thread(DownloadFile);//创建出来一个Thread对象,这个线程并没有启动t.Start();开始执行线程}
static void DownloadFile(object Name){ //参数类型必须是object类型Thread.Sleep(2000);Console.WriteLine("a");int id = Thread.CurrentThread.ManagedThreadId;}static void Main(string[] args){Thread t = new Thread(DownloadFile);//创建出来一个Thread对象,这个线程并没有启动t.Start("xxx");开始执行线程,并传递DownLoadFile的参数}

线程概念:后台线程和前台线程
使用Thread类创建的线程是前台线程,线程池中的线程总是后台线程
当所有前台线程结束,后台线程还未结束,后台线程会被终止掉

Thread t = new Thread(DownLoadFile);t.IsBackGround = true;//设置为后台线程t.Start("a");

线程的优先级

线程由操作系统调度,一个cpu同一时间只能做一件事,当有很多线程需要去执行时,cpu会根据优先级来执行线程
可以在Thread类中设置Priority属性,易影响现成的基本优先级,Priority属性是一个ThreadPriority枚举定义的一个值。其级别有:Highest,AboveNormalBelowNormal和Lowest。

控制线程
线程的状态(Running,Unstarted)当我们调用Thread对象的Start方法时,不是马上进入了Running状态,而是当系统线程调度器选择了要运行的线程,这个线程才会Running。

使用Thread对象的Abort()方法可以停止线程。调用之歌方法,会在终止要终止的线程中抛出一个ThreadAbortException类型的异常,我们可以try catch 这个异常

使用Thread对象的Join()方法//使当前的线程暂停,当前线程状态为WaitSleepJoin,直到加入进来的线程运行结束后再执行当前线程

线程池

创建线程需要时间,如果有不同的小任务要完成,就可以提前创建线程存于线程池中

static void ThreadMethod(object state){Console.WriteLine("线程开始"+Thread.CurrThread.ManagedThreadId);开启一个工作线程}static void Main(string[] args){ThreadPool.QueueUserWorkItem(ThreadMethod);}

线程池中的线程是后台线程不能改为前台线程,线程池只适合做一些小任务

通过任务的方式创建线程

一、
任务类 task

Task t = new Task(ThreadMethod);//实例一个任务对象,传递一个需要线程执行的方法t.Start();//开启任务

二、
任务工厂类

TaskFactory ft = new TaskFactory();Task t = tf.StartNew(ThreadMethod);

连续任务
如果任务1执行依赖于另一个任务2那么就需要在2执行后才执行1这个时候我们可以使用连续任务

Task t1 = new Task(function a );Task t2 = t1.ContinueWith(function b);//创建一个t1结束后的连续任务t2

任务的层次结构
我们在一个任务中启动一个新任务,相当于新任务是当前任务的子任务。两个任务异步执行。
当父任务结束子任务还未结束,他的状态会设置为WatingForChildrenToComplete如果子任务也结束,父任务的状态将变成RunToCompletion

线程问题 争用条件
当很多线程访问一个内存区域的时候会有冲突

解决这个问题就需要对当前线程加个锁,可以锁定引用类型,对象,不可以使用值类型

lock(m){//向系统申请可不可以锁定m对象。当对方正在锁,需要等到对方线程释放锁定后才可以锁定m对象}释放锁定

线程死锁
两个线程a,b需用使用两个内存地址1,2
a先锁1后锁2,b先锁2后锁1
a锁1时b锁2
a想锁2时2已经被b锁了
b想锁1时1已经被a锁了
这就是死锁。。。。

0 0