黑马程序员————————C#基础知识之多线程

 ---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ---------------------- 

l     概念

l     使用多线程

l     Thread

l     线程同步

l     使用线程的优缺点

l     关于非创建控件线程调用(++

l     线程调用带参数的方法

 1.概念:            

   进程:正在执行的应用程序,操作系统通过进程将正在运行的不同应用程序分开。

   线程:操作系统分配CPU处理时间的基本单元,也叫控制单元,每个线程都是一条执行代码的路径,每个进程都至少包含一个线程,当包含多个线程执行程序的时候即为多线程,通常把执行Main方法中代码的线程叫做主线程。

    多线程提供了多个代码执行路径,通过CPU快速切换处理,实现了宏观上的“同步”执行。微观上看,对于单核(一个CPU)计算机,在某一时刻(计算机时间单位,非常小),只能有一个线程运行。

2.使用多线程:

  原因:如果程序只有一个线程,也就是一条执行顺序时,应用程序很可能在执行大量后台数据时发生卡死现象,即发生与用户交互的UI界面无法响应等情况,使用多线程,可以通过建立另一条线程执行响应UI界面的代码,独立于执行后台数据的线程之外,优化应用程序。(通过多线程解决吞吐量和响应性问题

  创建方式

          1.准备好要执行的代码,并将其作为委托。

               2.引用线程类Thread的命名空间Threading.

               3.创建Thread类的对象,并将要执行的方法以参数形式传给对象。

               4.调用Thread类的对象的Start方法,标记该线程具备了被CPU执行的资格。(并不一定有执行权)

     //方法

       void method()

       {                

               for (int i=0; i < 1000; i++)

               {                   

                   Console.WriteLine(i +Thread.CurrentThread.Name);

               }

               MessageBox.Show("循环完毕。。。");      

       }

       //

       classA

       {

           publicvoid Amethod()

           {                             

                   for (int i=0; i < 1000; i++)

                   {

                       Console.WriteLine(i +Thread.CurrentThread.Name);

                   }

                   MessageBox.Show("循环完毕。。。");

           }

       }

       privatevoid btnthread_Click(object sender,EventArgs e)

       {

          

           //将方法作为委托做参数

   1.       ThreadStart th = new ThreadStart(method);    //   可以将1,2步简写为

   2.       Thread threadFirst = new Thread(th);         //  Thread threadFirst = new Thread(method);

           threadFirst.Name ="first";

           threadFirst.Start(); //开启一线程

           //创建对象并调用其方法做参数

           A thread =newA();

           Thread threadSecond =new Thread(thread.Amethod);

           threadSecond.Name ="second";          

           threadSecond.Start();  //开启二线程

       }

Thread.CurrentThread.Name  获取当前执行线程的名称。

前台线程:默认情况下,线程建立后为前台线程,只有所有前台线程的关闭,才能完成程序关闭。

后台线程:只要所有的前台线程结束,后台线程就自动结束。

一般将不需要显示在界面,或者不用与用户交互的线程设置为后台线程,通过调用对象的IsBackground属性来设置线程是否为后台线程,为True则为后台线程,false则为前台线程。

 

3.关于创建控制线程的类Thread

  1.Thread类有sealed修饰,说明该类不能被继承

  2.Thread类提供了静态方法SleepInt32/TimeSpan,挂起或阻塞线程参数量的时间。

  3.Thread类提供了静态方法 SpinWait(Int32),让线程等待参数量时间。

  4.Threadl类提供了实例方法About,可以终止线程。

4.线程同步(解决线程重入问题)

默认情况下每个线程执行的代码都是独立的,即使他们执行相同的代码,也会由每个线程各执行一次,为了解决这种问题,让多条线程只执行一次代码,我们需要同步线程,这里暂时只说一种简单快速的方法,lock语句锁;以及调用Monitor类的组方法;和仅作了解一般不使用的static修饰字段。

1))第一种lock方法:

 classTest

       {

           int i = 1000;

           

           publicvoid testThread()

           {

               while (true)

               {

                   object locker =newobject();

                   lock (locker)

                   {

                       if (i > 0)

                       {

                           Thread.SpinWait(10);   //等待10毫秒

                           i--;

                           Console.WriteLine(i +Thread.CurrentThread.Name + i);

                       }

                       else

                       {

                           MessageBox.Show("OVER");

                           break;

                       }

                   }

               }                            

           }

       }

       privatevoid btnthread_Click(object sender,EventArgs e)

       {

           Test test =newTest();

           Thread firstThread =new Thread(test.testThread);

           firstThread.Name ="one";

           firstThread.Start();

           Thread secondThread =new Thread(test.testThread);

           secondThread.Name ="two";

           secondThread.Start();

           Thread thirdThread =new Thread(test.testThread);

           thirdThread.Name ="three";

           thirdThread.Start();

       }

Lock语句保证了三条线程执行同一资源时不发生冲突,有几点是使用过程中必须注意的。

1.像如上测试时,线程名称必须取英文,中文有可能发生输出错误。

2.测试lock语句中的等待时间间隔应该用SpinWait方法而不是Sleep方法,原因是Sleep方法会使当前线程放弃CPU执行权,失去了lock锁的作用。

3.对于lock锁中的循环尽量使用如上的while循环,而不是for循环,因为可能发生未知错误。

2))第二种调用Monitor类的方法:

 classTest

       {

           int i = 1000;           

           publicvoid testThread()

           {

               while (true)

               {

                   object locker =newobject();

                   Monitor.Enter(locker);

                   try

                   {

                       if (i > 0)

                       {

                           Thread.SpinWait(10);

                           i--;

                           Console.WriteLine(i +Thread.CurrentThread.Name + i);

                       }

                        else

                       {

                           MessageBox.Show("OVER");

                           break;

                       }

                   }

                   finally

                   {

                     Monitor.Exit(locker);

                   }                                          

               }                            

           }

       }

3))对于如上这种多线程执行代码都是为了改变变量i的情况,还可以用static来定义字段(但也要遵循lock语句使用要点),例如:staticint i = 10000;这样也可以保证多线程运行的数据同步,但是一般我们不这么使用,因为静态字段的生命周期过长,不利于资源使用。

5.多线程的优点和缺点(引用自MSDN

1))多个线程的优点

无论如何,要提高对用户的响应速度并且处理所需数据以便几乎同时完成工作,使用多个线程是一种最为强大的技术。在具有一个处理器的计算机上,多个线程可以通过利用用户事件之间很小的时间段在后台处理数据来达到这种效果。例如,在另一个线程正在重新计算同一应用程序中的电子表格的其他部分时,用户可以编辑该电子表格。

无需修改,同一个应用程序在具有多个处理器的计算机上运行时将极大地满足用户的需要。单个应用程序域可以使用多个线程来完成以下任务:

l      通过网络与 Web 服务器和数据库进行通信。

l      执行占用大量时间的操作。

l      区分具有不同优先级的任务。例如,高优先级线程管理时间关键的任务,低优先级线程执行其他任务。

l      使用户界面可以在将时间分配给后台任务时仍能快速做出响应。

2))多个线程的缺点

建议您使用尽可能少的线程,这样可以最大限度地减少操作系统资源的使用,并可提高性能。线程处理还具有在设计应用程序时要考虑的资源要求和潜在冲突。这些资源要求如下所述:

系统将为进程、AppDomain 对象和线程所需的上下文信息使用内存。因此,可以创建的进程、AppDomain 对象和线程的数目会受到可用内存的限制。

跟踪大量的线程将占用大量的处理器时间。如果线程过多,则其中大多数线程都不会产生明显的进度。如果大多数当前线程处于一个进程中,则其他进程中的线程的调度频率就会很低。

使用许多线程控制代码执行非常复杂,并可能产生许多 bug。

销毁线程需要了解可能发生的问题并对那些问题进行处理。

提供对资源的共享访问会造成冲突。为了避免冲突,必须对共享资源进行同步或控制对共享资源的访问。如果在相同或不同的应用程序域中未能正确地使访问同步,则会导致出现一些问题,这些问题包括死锁和争用条件等,其中死锁是指两个线程都停止响应,并且都在等待对方完成;争用条件是指由于意外地出现对两个事件的执行时间的临界依赖性而发生反常的结果。系统提供了可用于协调多个线程之间的资源共享的同步对象。减少线程的数目使同步资源更为容易。

需要同步的资源包括:

l      系统资源(如通信端口)。

l      多个进程所共享的资源(如文件句柄)。

l      由多个线程访问的单个应用程序域的资源(如全局、静态和实例字段)。

3))线程处理与应用程序设计

一般情况下,要为不会阻止其他线程的相对较短的任务处理多个线程并且不需要对这些任务执行任何特定调度时,使用 ThreadPool 类是一种最简单的方式。但是,有多个理由创建您自己的线程:

l      如果您需要使一个任务具有特定的优先级。

l      如果您具有可能会长时间运行(并因此阻止其他任务)的任务。

l      如果您需要将线程放置到单线程单元中(所有 ThreadPool线程均处于多线程单元中)。

l      如果您需要与该线程关联的稳定标识。例如,您应使用一个专用线程来中止该线程,将其挂起或按名称发现它。

l      如果您需要运行与用户界面交互的后台线程,.NET Framework 2.0 版提供了 BackgroundWorker 组件,该组件可以使用事件与用户界面线程的跨线程封送进行通信。

4))线程处理和异常

在线程中执行异常处理。线程(甚至是后台线程)中的未处理异常通常会终止进程。以下为此规则的三种例外情况:

l      由于调用了 Abort,线程中将引发 ThreadAbortException。

l      由于正在卸载应用程序域,线程中将引发 AppDomainUnloadedException。

l         公共语言运行库或宿主进程将终止线程。

6.跨控件线程调用

    从不是控件创建的线程访问该控件可以通过设置属性 CheckForIllegalCrossThreadCalls =false;(不检查这种不安全线程)来调用该控件。(++     

7.线程执行带参数的方法

  1))传递单个参数:

      要求被执行的方法传递的参数为object类型

          publicdelegatevoid ParameterizedThreadStart(object obj);

例如:

 classTest

       {

           int i = 1000;

 

           publicvoid testThread(object name)

           {

               while (true)

               {

                   object locker =newobject();

                   lock (locker)

                   {

                       if (i > 0)

                       {

                           Thread.SpinWait(10);   //等待10毫秒

                           i--;

//使用参数时,name转换成string类型

                           Console.WriteLine(i +Thread.CurrentThread.Name + i+name.ToString());

                       }

                       else

                       {

                           MessageBox.Show("OVER");

                           break;

                       }

                   }

               }

           }

       }

       privatevoid btnthread_Click(object sender,EventArgs e)

       {

           Test test =newTest();

           Thread firstThread =new Thread(test.testThread);

           firstThread.Name ="one";

           //在开启线程时传递参数

           firstThread.Start("firstThread");

           Thread secondThread =new Thread(test.testThread);

           secondThread.Name ="two";

           secondThread.Start("secondThread");

           Thread thirdThread =new Thread(test.testThread);

           thirdThread.Name ="three";

           thirdThread.Start("thirdThread");

       }

要求被调用方法中传参类型为object类型,在方法中执行时再转换为需要的类型,在调用的线程的start方法中传递参数。当然,即便是不在start中传递参数,线程也会启动。

2))传递多个参数:

可以用集合来传递和接受参数。

 void TxtShow(object name)

       {

          List<string> list=nameasList<string> ;

           object obj =newobject();

               lock (obj)

               {

                   //如果没有null判断,将会出现未将对象引用设置到对象实例的错误

                   if (list !=null)

                   {

                       foreach (string iin list)

                       {

                           Console.WriteLine(i+Thread.CurrentThread.Name);

                       }

                   }

                   else

                   {

                       Console.WriteLine("null");

                   }

               }                   

       }                         

       privatevoid btnstart_Click(object sender,EventArgs e)

       {                 

           CheckForIllegalCrossThreadCalls =false;

           Thread startThread =new Thread(TxtShow);

           startThread.Name ="startThread";

           startThread.Start(newList<string>() {"张三","李四","王五"});           

           Thread overThread =new Thread(TxtShow);

           overThread.Name ="overThread";

           overThread.Start();

       }

输出:

可见,CPU的执行权还没交给overThread线程,程序已经执行结束,泛型集合中包含了null元素。

 ----------------------ASP.Net+Android+IOS开发.Net培训、期待与您交流! ---------------------- 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值