hi,你好!欢迎访问本站!登录
本站由简数采集腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

【C#多线程】1.Thread类的运用及注重要点

2019-11-18杂谈搜奇网25°c
A+ A-

Thread随意讲讲

  由于在C#中,Thread类在我们的新营业上并不常常使用了(由于建立一个新线程要比直接从线程池拿线程越发消耗资本),而且在.NET4.0后新增了Task类即Async与await关键字,使得我们基础不再用Thread了,不过在进修多线程前,有必要先了解下Thread类,这里就先随意讲讲Thread。

1.运用多线程的几种体式格局

  多线程Thread类只支撑运转两种要领,一种是无参数而且无返回值的要领,第二种是有一个Object范例参数(有且只能有一个参数,而且必需是Object范例)且无返回值的要领。假如想让多线程要领照顾多个参数,能够将多个参数放入一个鸠合或数组中传入要领。

  下面例子运用了控制台来演示多线程的简朴运用:

using System; using System.Threading; namespace ConsoleApplication1 { class Program { //无参数无返回值要领
        public static void DoSomething() { for (int i = 0; i < 100; i++) { Thread.Sleep(500); } } //有参数无返回值要领
        public static void DoSomethingWithParameter(object obj) { for (int i = 0; i < (int)obj; i++) { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); Thread.Sleep(500); } } static void Main(string[] args) { //猎取主线程ID
            int currentThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"---------主线程<{currentThreadId}>最先运转---------"); //多线程运转无参数要领体式格局1
            ThreadStart ts = DoSomething;//ThreadStart是一个无参数,无返回值的托付
            Thread thread1 = new Thread(ts); thread1.Start(); //多线程运转无参数要领体式格局2
            Thread thread2 = new Thread(DoSomething);//可省略ThreadStart
 thread2.Start(); //多线程运转有参数要领体式格局1 //ParameterizedThreadStart是一个有一个Object范例参数,然则无返回值的托付。
            ParameterizedThreadStart pts = DoSomethingWithParameter; Thread thread3 = new Thread(pts); thread3.Start(100); //多线程运转有参数要领体式格局2 //能够省略ParameterizedThreadStart
            Thread thread4 = new Thread(DoSomethingWithParameter); thread4.Start(100); //还能够运用lamda表达式简化多线程写法
            new Thread(() => { for (int i = 0; i < 100; i++) { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); Thread.Sleep(500); } }).Start(); Console.WriteLine($"---------主线程<{currentThreadId}>运转终了---------"); } } }

  运转结果以下:

  

 

 2.前台线程与背景线程

  • 前台线程

  如主线程(或称为UI线程)就是前台线程,默许Thread的实例均为前台线程,前台线程的特点是,假如当前运用的前台线程没有悉数运转终了,那末当前运用就没法退出。举个例子,我们晓得平常状况下,控制台运用在Main要领终了后会自动终了当前历程,假如我们在Main要领中建立了一个新Thread线程,并使其坚持运转,那末纵然Main要领实行终了,控制台历程也没法自动封闭(除非手动右上角点×)。就以下图状况,画红圈的处所示意Main要领实行终了,但是顺序照旧在运转,所以我们平常在用Thread的时刻会将Thread设置为背景线程。

 

  • 背景线程

  背景线程与前台线程的唯一区别是,它不会去影响顺序的生老病死,当顺序的前台线程悉数封闭(即顺序退出),那末纵然顺序的背景线程照旧在实行使命,那末也会强迫封闭。

  设置Thread为背景线程的体式格局:

        Thread tt = new Thread(DoSomething); tt.IsBackground = true;//设置tt为背景线程
        tt.Start();

  前台线程与背景线程对顺序的影响结果看似彷佛不算大,然则假如我们在做Winform或许WPF项目时,若在某窗体内实行一个新线程使命(这个新线程是前台线程),假如在使命实行时期封闭顺序,此时会发明,虽然界面都被封闭,然则计算机使命管理器中此顺序照旧还在运转(而且假如在新线程中实行的使命异常致使线程没法封闭,那末这个顺序就会一直在背景跑下去),再次开启顺序可能会致使打不开等效果,这类行动是异常不好的。所以我们平常运用多线程Thread类时,最好随手将它设置为背景线程。我们能够举个例子。

        static void Main(string[] args) { //猎取主线程ID
            int currentThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"---------主线程<{currentThreadId}>最先运转---------"); //实行一个也许能够运转50秒的新线程
            Thread t = new Thread(() => { for (int i = 0; i < 100; i++) { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); Thread.Sleep(500); } }); t.IsBackground = true;//设置t为背景线程
 t.Start(); Console.WriteLine($"---------主线程<{currentThreadId}>运转终了---------"); }

  这个例子的运转结果就不截图了,由于控制台会一闪而过(马上实行完Main要领便封闭),纵然背景线程t还在实行使命,然则也会强迫封闭。

 

3.让主线程守候新线程实行完成后再继承实行(运用Thread的Join要领)

  直接上代码:

        static void Main(string[] args) { //猎取主线程ID
            int currentThreadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"---------主线程<{currentThreadId}>最先运转---------"); //实行一个也许能够运转50秒的新线程
            Thread t = new Thread(() => { for (int i = 0; i < 20; i++) { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); Thread.Sleep(500); } }); t.IsBackground = true;//设置t为背景线程
 t.Start(); t.Join();//在t线程实行时期,假如主线程挪用t线程的Join要领,主线程会卡在这个处所直到t线程实行终了
 Console.WriteLine($"---------主线程<{currentThreadId}>运转终了---------"); }

 

4.Thread实例的其他常常使用要领

  直接看代码解释吧:

        static void Main(string[] args) { //实行一个也许能够运转50秒的新线程
            Thread t = new Thread(DoSth); t.IsBackground = true;//设置t为背景线程
 t.Start(); t.Join();//在t线程实行时期,假如主线程挪用t线程的Join要领,主线程会卡在这个处所晓得t线程实行终了
            t.Priority = ThreadPriority.Normal;//设置线程调理的优先级
            ThreadState rhreadState = t.ThreadState;//猎取线程运转状况。
            bool b = t.IsAlive;//猎取线程当前是不是存活
            t.Interrupt();//中断当前线程
            t.Abort();//停止线程 
        }

 

5.Thread类的常常使用要领

  直接看代码解释吧:

        static void Main(string[] args) { //使得当前线程停息1秒再继承实行,此处会停息主线程1秒钟 //假如写在其他线程实行的要领中,会让实行谁人要领的线程停息1秒再继承实行)
            Thread.Sleep(1000); //猎取当前实行线程的线程实例
            Thread t = Thread.CurrentThread; }

  

6.运用多线程须要注重的要点

   (1)子线程不能够直接挪用UI线程(即主线程)的UI对象,然则能够挪用在主线程自定义的对象

  我们在做Winform或WPF开辟时,比方在前端有一个TextBox文本框,其Name属性为textBox,那末假如我们在此窗体内开启了个子线程,并在子线程内对textBox.Text赋值,是会报错的,由于子线程没法接见主线程的UI元素(本质是UI元素必需由建立它的线程去操纵)。

  以下代码,子线程操纵主线程建立的对象时不会报错,然则子线程操纵主线程建立的UI对象时会报错:

        private void button1_Click(object sender, EventArgs e) { Student stu = new Student();//主线程建立的Student类实例
            new Thread(() => { stu.Name = "ccc";//子线程操纵主线程建立的对象并不会报错。
                textBox1.Text = "abc";//子线程直接挪用UI线程textBox1会报错
 }).Start(); }

 

   处置惩罚思绪:在子线程想操纵UI线程的UI元素时,呼唤主线程去操纵即可,代码以下:

        delegate void DoSth(string str);//建立一个托付
        public void SetTextBox(string str)//建立一个托付要领用于转变主线程textBox的值
 { textBox1.Text = str; }
     //按钮点击事宜
private void button1_Click(object sender, EventArgs e) {
       //在子线程内实行....
new Thread(() => { //----------------------细致写法------------------------ DoSth delegateMethod = new DoSth(SetTextBox);//建立要领的托付 //this指当前Window //this.Invoke指让建立窗体的线程实行某托付要领 //第二个参数是传入托付要领即SetTextBox的参数 this.Invoke(delegateMethod, "abc"); //----------------------简写体式格局---------------------- this.Invoke(new Action(() => { textBox1.Text = "abc";//子线程直接挪用UI线程textBox1会报错 })); }).Start();

  补充:上面代码是Winform跨线程操纵UI元素的常常使用体式格局,那末WPF怎样跨线程操纵UI呢?直接看下面代码吧

    //体式格局1(常常使用):猎取当前运用的UI线程,实行某要领
    App.Current.Dispatcher.Invoke(() => { textBox1.Text="abc" }); //体式格局2(只能在this是当前Window或能够猎取到窗体实例的状况下运用):
    this.Dispatcher.Invoke(new Action(()=> { textBox1.Text="abc" })); 

  (2)多线程同时接见一个资本时,要注重同步题目。

  比方两个及以上的线程同时接见一个资本(能够是文件,能够是对象),假如没有注重同步题目,会致使以下题目。直接看代码

        private void button1_Click(object sender, EventArgs e) { int num = 0; //建立两个线程对num举行累加,各加100000,理论上线程实行终了后末了的值应该是200000
            Thread t1 = new Thread(() => { for (int i = 0; i < 100000; i++) { num++; } }); Thread t2 = new Thread(() => { for (int i = 0; i < 100000; i++) { num++; } }); //两个子线程同时实行
 t1.Start(); t2.Start(); //守候t1与t2线程实行终了再继承实行
 t1.Join(); t2.Join(); Console.WriteLine(num.ToString());//输出num的值 }

  结果:

  结果并非200000,由于t1与t2线程同时对num举行自增操纵时刻,常常会涌现t1读取到了num为99,自增1,结果100赋值给num,然则在t1刚读取到num值而且还没举行自增操纵时,t2也读取到了num为99,自增下也是100赋值给num。也就是说t1与t2举行了雷同的操纵。

  

 

   怎样防止当前这个题目呢?就须要在多线程接见一个资本时,举行资本同步处置惩罚。那什么是同步呢?同步是指我用完了你才能用,你我不能同时运用一个资本。这个题目的细致处置惩罚要领会在该系列今后的博客中写。

 

  

  下节我们会简朴讲讲线程池+之前的两种异步处置惩罚机制。

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
【C#多线程】1.Thread类的运用及注重要点

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/282352.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>