C#线程与多线程

8 篇文章 0 订阅

11.02: 

Thread具有的属性:
   IsAlive
   Thread.Name:只能设置一次,一般用于调试
   ThreadState : ,Unstarted,Stopped,Running,WaitSleepJoin, 
抢占:被另外一个线程交织
Thread.CurrentThread
Thread.Join(); 等待此线程执行完,才往下执行。
Thread.Join(ms); 添加超时
Thread.Sleep();暂停当前的线程,并等一段时间

Thread.Sleep(0); 这样调用会导致线程线程立即放弃本身当前的时间片,自动将CPU移交给其他线程
 Thread.Yield()做同样的事情,但它只会把执行交给同一处理器上的其他线程
 当等待Sleep或Join的时候,线程处于阻塞的状态

解除阻塞的条件:
  1.阻塞条件被满足
  2.操作超时(如果设置超时的话)
  3.通过Thread.Interrupt()进行打断
  4.通过Thread.Abort()进行中止
  
上下文切换:

I/O Bound
CPU Bound : 一般自觉

Blocking 杂Spinning 

SpinLock, Spinning

线程:【C#进阶系列】25 线程基础 - 韩子卢 - 博客园

在多线程中实现线程同步的是线程同步构造,这个构造分两大类,一个是基元构造,一个是混合构造。所谓基元则是在代码中使用最简单的构造。基原构造又分成两类,一个是用户模式,另一个是内核模式。而混合构造则是在内部会使用基元构造的用户模式和内核模式,使用它的模式会有一定的策略,因为用户模式和内核模式各有利弊,混合构造则是为了平衡两者的利与弊而设计出来。下面则列举整个线程同步体系结构
基元
 1.1 用户模式
    1.1.1 volatile
    1.1.2 Interlock
 1.2 内核模式
    1.2.1 WaitHandle
    1.2.2 ManualResetEvent与AutoResetEvent
    1.2.3 Semaphore
    1.2.4 Mutex
混合
 2.1 各种Slim
 2.2 Monitor
 2.3 MethodImplAttribute与SynchronizationAttribute
 2.4 ReaderWriterLock
 2.5 Barier(少用)
 2.6 CoutdownEvent(少用)

线程分类:前台线程 和 后台线程; 默认是前台线程(主线程就是一个前台线),可通过IsBackground 来查询和设置;
          区别在进程中,只要有一个前台线程未退出,进程就不会终止。而后台线程不管线程是否结束,只要所有前台线程都退出(包括正常退出和异常退出)后,进程就会自动终止。

   ///测试IsBackground属性 
   static void Main(string[] args)
	{
		Thread thread = new Thread(ThreadProc);
		thread .IsBackground = true; //由于设置成后台线程,所以只要所有的前台线程停止运行,进程就停止
		thread .Start("hello");
		Console.WriteLine("某线程运行开始");
		Console.WriteLine("继续运行");
	}
	private static void ThreadProc(Object stateInfo)
    {
		Thread.Sleep(10000);
		if (stateInfo.GetType() == typeof(string))
		{
			Console.WriteLine("这是一个字符串");
		}
		else {
			Console.WriteLine("未识别");
		}
	}

线程模型: .NET支持两种线程模型:STA(single threaded apartments) 和 MTA(multi threaded apartments) ,详见
                   缺省状态下是MTA 模型。
   
创建方式 :1.手动创建;

   2.利用.net framework的特性来创建线程,如:
     后台工作线程(BackgroundWorker) : WindForm用的
     线程池(ThreadPool)、
     线程计数器、
    Remoting服务器、
    一个网络服务器;
    一个ASP.NET服务器

        线程池都是后台线程.

  线程属性决定了线程的执行时间,这里说的是多线程,且跟同一应用中其他活动线程相关的,级别有

      enum ThreadPriority{Lowest, BelowNormal, Normal, AboveNormal, Highest}
  多线程时,把线程属性设置的高并不意味着它能够实时工作,它还受应用程序的程序属性限制,所以
  需要把Process设置成最高配置属性(Realtime):Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

  常用方法:

   t.Abort();//终止这个t线程的执行   
   Thread.Sleep(0);//放弃CPU的时间片,停止对CPU的占用,以使其他在时间片队中活动的线程被执行
   Thread.Sleep(1000); //线程停止1000毫秒
   Thread.Sleep(TimeSpan.FromHours(1)); //停止1小时
   Thread.Sleep(Timeout.Infinite); //线程停止到被打断
   t.Join(); //让当前线程睡眠,等待t线程执行完,然后继续运行下面的代码

EventWaitHandle : 表示一个线程同步事件  
   Reset() :将事件状态设置为非终止,从而导致线程受阻。
   Set() :将事件状态设置为有信号,从而允许一个或多个等待线程继续执行。
   WaitOne(): 阻止当前线程,直到当前 WaitHandle 收到信号。
AutoResetEvent : 继承于EventWaitHandle,表示线程同步事件在一个等待线程释放后收到信号时自动重置
  
WaitCallback : Represents a callback method to be executed by a ThreadPool thread
   定义; public delegate void WaitCallback(object stateInfo);

Mutex : 互斥锁是一个互斥的同步对象,线程间同步的基元(primitive),也可用于进程间的同步。
    public sealed class Mutex : System.Threading.WaitHandle

///互斥锁 Mutex 样例
class ShareRes
{
	public static int count = 0;
	public static Mutex mutex = new Mutex(); //无参数,表示创建一个处于未获取状态的互斥锁
}

class IncThread
{
	int number;
	public Thread thrd;
	public IncThread(string name, int n)
	{
		thrd = new Thread(this.run);
		number = n;
		thrd.Name = name;
		thrd.Start();
	}
	
	void run()
	{
		Console.WriteLine("{0}正在等待 the mutex", thrd.Name);
		ShareRes.mutex.WaitOne();
		Console.WriteLine("{0}申请到 the mutex", thrd.Name);
		do{
			Thread.Sleep(1000);
			ShareRes.count++;
			Console.WriteLine("In {0} ShareRes.count is {1}", thrd.Name, ShareRes.count);
			number--;
		}while(number > 0);
		
		Console.WriteLine("{0}释放 the mutex", thrd.Name);
		ShareRes.mutex.ReleaseMutex();
	}
}

class DecThread
{
	int number;
	public Thread thrd;
	public DecThread(string name, int n)
	{
		thrd = new Thread(this.run);
		number = n;
		thrd.Name = name;
		thrd.Start();
	}
	void run()
	{
		Console.WriteLine(thrd.Name + "正在等待 the mutex");
		ShareRes.mutex.WaitOne(); //申请
		Console.WriteLine(thrd.Name + "申请到 the mutex");
		do
		{
			Thread.Sleep(1000);
			ShareRes.count--;
			Console.WriteLine("In " + thrd.Name + "ShareRes.count is " + ShareRes.count);
			number--;
		} while (number > 0);
		Console.WriteLine(thrd.Name + "释放 the nmutex");
		ShareRes.mutex.ReleaseMutex(); //释放
	}
 }
 
 class TestMutex{
	 static void Main()
	 {
		 IncThread mthrd1 = new IncThread("IncThread thread ", 5);
		 DecThread mthrd2 = new DecThread("DecThread thread ", 5);
		 mthrd1.thrd.Join();  //让当前线程睡眠,等待mthrd1.thrd线程执行完,然后继续运行下面的代码
		 mthrd2.thrd.Join();
	 } 
 }

 

Task.WaitAny();
Task.WaitAll();
Task.WhenAny(); //当某线程结束时,程序继续往下执行

/************* ManualResetEvent + CancellationToken的使用  ********************/

CancellationTokenSource tokenSource;
ManualResetEvent m;
void BtnClickHandle() {
    tokenSource = new CancellationTokenSource();
    CancellationToken cancelToken = tokenSource.Token;   
    m = new ManualResetEvent(true); //true表示线程启动后(一上来),就收到完成信号,不阻塞,继续往下执行
	
	Task.Factory.StartNew(()=>{
	   for(int i=0;i<100;i++)
	   {
			if(cancelToken.IsCancellationRequested) 
				return;
			m.WaitOne();
			//do sth. handle in multiThread
			Thread.Sleep(500); //模拟多线程长时间处理
	   }
	}, cancelToken);

}

m.Reset(); //暂停信号,让多线程阻塞
m.Set(); //给(手动事件)一个已经完成的true信号,那么WaitOne那里就不阻塞了.

取消某个线程
  cancelToken.Cancel(); //马上取消线程
  cancelToken.CancelAfter(3000); //3秒后取消线程

Monitor : lock本质上是调用Monitor

ThreadPool : 线程池,可看作是容纳线程的容器;它是个静态类,通过QueueUserWorkItem()方法将工作函数排入线程池; 每排入一个工作函数,就相当于请求创建一个线程;所产生的都是后台线程,不能修改为前台线程。

 每个CLR一个线程池,这个线程池由CLR上所有的AppDomain共享;

线程池的作用:

1、线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
2、如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。

   

///ThreadPool 样例
public static void Main()
{
	//声明一个Action委托的List,添加一些委托测试用
	List<Action> actions = new List<Action>
	{
		()=>{Console.WriteLine("A-1");},
		()=>{Console.WriteLine("A-2");},
		()=>{Console.WriteLine("A-3");},
		()=>{Console.WriteLine("A-4");}
	};
	 
	for(int i=0;i<actions.Count; )
	{
		Thread.Sleep(1); //因为主线程的遍历很快的,而线程池的任务执行要时间的(需要(创建)启动和请求)
		ThreadPool.QueueUserWorkItem(state => actions[i](), null);
		i++;
	}
	Console.ReadKey();
}
	
static void ThreadProc(Object stateInfo)
{
	Console.WriteLine("Hello From the thread pool.");
}

Semaphore:可理解为允许线程执行信号的池子,池子中放入多少个信号就允许多少线程同时执行。

   Semaphore semaphore = new Semaphore(0, 1);
 
   Thread thread1 = new Thread(() =>
   {
        //线程首先WaitOne等待一个可用的信号量
        semaphore.WaitOne();
        //在得到信号量后,执行下面代码内容
        Console.WriteLine("thread1 work");
        Thread.Sleep(5000);
        //线程执行完毕,将获得信号量释放(还给semaphore)
        semaphore.Release();
   });
 
   Thread thread2 = new Thread(() =>
   {
        semaphore.WaitOne();
        Console.WriteLine("thread2 work");
        Thread.Sleep(5000);
        semaphore.Release();
   });

   thread2.Start();
   thread1.Start();
   //因在创建Semaphore时拥有的信号量为0
   //semaphore.Release(1) 为加入1个信号量到semaphore中
   semaphore.Release(1);

     

BackgroundWorker : 允许您在单独的线程上执行某个可能导致用户界面(UI)停止响应的耗时操作(比如文件下载数据库事务等),并且想要一个响应式的UI来反应当前耗时操作的进度。 
可以看的出来,BackgroundWorker组件提供了一种执行异步操作(后台线程)的同时,并且还能妥妥的显示操作进度的解决方案
DoWork事件: 用于承载异步操作; 当调用BackgroundWorker.RunWorkerAsync()时触发。 
注意: 由于DoWork事件内部的代码运行在非UI线程之上,所以在DoWork事件内部应避免于用户界面交互,而于用户界面交互的操作应放置在ProgressChanged和RunWorkerCompleted事件中。

见:C# BackgroundWorker使用总结 - 五维思考 - 博客园

TAP:基于Task任务的异步编程

 Task.FromResult的作用:用来创建一个带返回值的、已完成的Task
   1.虽然以同步的方式实现该接口方法,但要返回异步的结果
   2.从缓存中获取值,以同步或者异步的方式实现:
     
       //异步获取缓存的方法
        private async Task<string> GetValueAsync(int key)
        {
            string result = await SomeAsyncMethod();
            cache.TrySetValye(key, result);
            return result;
        }
        
        //从本地缓存中获取值是同步的方式,但方法返回的类型是异步的Task,通过Task.FromResult(result)返回了异步结果
        public Task<string> GetValueFromCache(int key)
        {
            string result = string.Empty;
            if(cache.TryGetValue(key, out result))
            {
                return Task.FromResult(result);
            }
            return GetValueAsync(key);
        }
   另外,如果使用Task.FromResult不带返回值,就使用Task.FromResult(0) 或 Task.FromResult(null)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值