线程,任务,线程池的学习总结

同步操作(synchornous operation):先完成其工作再返回调用者。
异步操作(asynchornous operation):大部分工作是在返回给调用者之后才完成的,但是线程异步必须对资源进行协调,否则两个或者更多线程可能在同一时间访问相同的资源,二每个线程都不知道其他线程的操作,结果将产生不可预知的数据损坏。异步需要并发的创建,我们学习的异步方法通常有:Thread.Start和Task.Run还有Dispatcher.BeginInvoke,SynchronizationContext.Post。

富客户端应用程序的线程

在WPF,UWP,Windows Forms应用程序中,在主线程上执行长时间的操作将导致应用程序失去响应。一个常用的方法是创建一个工作线程来执行耗时的操作(通常用异步),并在完成之时更新UI线程,这种方法就要采用封送技术,实现这个操作的底层模式有:
1.WPF中,调用元素上的Dispatcher对象的BeginInvoke或者Invoke方法,这两者的区别看博客
2.在Windows Forms中:调用控件的BeginInvoke或者Invoke方法。
3.在UWP中:可以调用Dispatcher对象的RunAsync或者Invoke方法。
Invoke 或者 BeginInvoke 去调用,分别表示同步和异步,两者的区别就是一个导致工作线程等待,而另外一个则不会,线程同步时锁的使用可以参考这篇博客
UI线程也可以有多个,但每一个线程要对应不同的窗口。这样使每一个窗口指定独立的UI线程,可以让每一个窗口都具有更好独立响应的能力。

线程Thread

关于的Thread的使用可以看以前的博客点击这里
对线程的操作有创建,暂停(挂起),恢复,休眠和终止以及设置优先权。

这里讲述它的缺点:
1.线程完成后,就无法通过再次使用Start方法来重新启动它,相反只能将其Join(它告诉操作系统暂停执行当前线程,直到另一个线程终止)
2.直接使用线程会对性能产生影响,线程是昂贵的资源,上下文的切换不是免费的,如果需要运行大量并发的I/O密集型操作,线程本身的开销非常巨大。
3.线程除非创建共享字段,不然在Join之后难以得到“返回值”,此外捕获和处理线程中的操作抛出的异常也非常麻烦。

注意:如果线程里有while循环,会照成主界面卡顿

线程池

线程池是多个线程的集合,通过一定的逻辑决定如何为线程分配工作,有任务要执行时,它分配池中的一个工作者线程执行任务,线程池中的线程执行完指定的方法后并不会自动消除,而是以挂起状态返回线程池,如果应用程序再次向线程池发出请求,那么处以挂起状态的线程就会被激活并执行任务,而不会创建新线程,这就节约了很多开销。只有当线程数达到最大线程数量,任务会排队,等待其他任务释放线程后再执行。因此,使用线程池可以避免大量的创建和销毁的开支,具有更好的性能和稳定性,但是ThreadPool不能控制线程的执行顺序,我们不能有效监控和控制线程池中的线程。
所以它的效率是通过重用线程获得的。
但是线程池不包括需要长时间运行的工作。这种情况下使用任务并行库(Task Parallel Library,TPL),以后博客介绍。

public const int Repetition = 800;
        public  static void Main()
        {
            ThreadPool.QueueUserWorkItem(DoWork, '+');//将方法排入队列以便执行,并指定包含该方法所用数据的对象。 此方法在有线程池线程变得可用时执行。
            for (int count = 0; count < Repetition; count++)
            {
                Console.Write('-');
            }
        }
        public static void DoWork(object state)
        {
            for (int count = 0; count < Repetition; count++)
            {
                Console.Write(state);
            }
           
        }

注意:避免将线程池中的工作者线程分配给I/O受限(是从外部来源获取数据如磁盘驱动器,web服务器所产生的延迟)或长时间运行的任务,这时可以改用任务并行库(TPL)
需要注意:

  1. 线程池中的所有线程都是后台线程。如果进程的所有前台线程都结束了,所有的后台线程也都会停止。
  2. 不能将入池的线程改为前台线程。
  3. 不能给入池的线程设置优先级或名称。
  4. 入池的线程只能用于时间较短的任务。如果线程要一直运行(比如Word的拼写检查线程),就应该使用Thread类创建一个线程。

任务Task

Task类可以解决所有Thread的问题,通过Task“任务并行库”中的类轻松输入线程池
**任务是对象,其中封装了以异步方式执行的工作(委托也是封装了代码的对象,但是委托是同步的,而任务是异步的),所以不会阻塞线程。
以下实例描述任务如何获取一个Action并以异步方式
运行它

const int Rept = 800;
            Task task = Task.Run(()=>
            {
                for (int count = 0; count < Rept; count++)
                {
                    Console.Write('-');
                }
            });
            for(int count=0;count<Rept;count++)
            {
                Console.Write('+');
            }
            Task.Wait();//强迫主线程等待分配给任务的所有工作完成。相当于在工作者线程(自己创建的那个)上调用Join()

以下实例描述执行的任务返回结果

static void Main(string[] args)
{  
   Task<string> task2 = Task.Run<string>(() => TempClass.TempWay(10));
   Console.WriteLine(task2.Result);//读取Result属性自动造成当前线程阻塞,所以不用wait()
   System.Diagnostics.Trace.Assert(task2.IsCompleted);
}
class TempClass
{
    public static string TempWay(int data)
    {
        string tempstring = data.ToString();
        return tempstring;
    }
}

启动任务将从线程池获取一个新线程,创建第二个“控制点”,并在线程上执行委托(理解为任务是通过线程池来工作的)。
Task的背后的实现也是使用了线程池线程,但它的性能优于ThreadPool,因为它使用的不是线程池的全局队列,而是使用的本地队列,使线程之间的资源竞争减少。同时Task提供了丰富的API来管理线程。但是相对前面的两种耗内存,Task对于多核的CPU性能远超前两者,单核的CPU三者的性能没什么差别。
术语
为解决CPU核少线程多的矛盾,操作系统通过时间分片的机制来模拟多个线程并发运行。
Task某认使用线程池中的线程,他们都是后台线程。这意味着当主线程结束时,所有的任务也会随之停止。
调用Task的wait方法可以阻塞当前方法,直到任务完成,类似于线程对象的Join方法。
某认情况下,CLR会将任务运行在线程池上,这种线程非常适合执行短小的计算密集任务。如果要执行长时间的阻塞操作,则可以按照以下方式避免使用线程池中的线程。

 Task myTask = Task.Factory.StartNew(() =>。。。。,TaskCreationOptions.LongRunning)

在某个核心上更改执行线程的行动称为上下文切换。

同步上下文

它适用于所用富客户端用户界面的API

 using System.ComponentModel;
 SynchronizationContext _usingSync;
        public MainWindow()
        {
            InitializeComponent();
            _usingSync = SynchronizationContext.Current;
            new Thread(Work).Start();

        }
        void Work()
        {
            _usingSync.Post(_ => TextBox1.Text="111",null);
        }

Post的效果和BeginInvoke是相同的。

前台线程和后台线程

显式创建的线程是前台线程。只要前台线程中的任何一个正在运行,它就可以使应用程序保持活动状态,而后台线程则不会。一旦所有前台线程完成,应用程序结束,所有仍在运行的后台线程终止。

IsBackground = true

参考:
C#多线程和异步1——基本概念和使用方法
C#多线程和异步2–TaskHE async/await详解

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值