zoukankan      html  css  js  c++  java
  • C#实现多线程的方法:线程(Thread类)和线程池(ThreadPool)

    简介

      使用线程的主要原因:应用程序中一些操作需要消耗一定的时间,比如对文件、数据库、网络的访问等等,而我们不希望用户一直等待到操作结束,而是在此同时可以进行一些其他的操作。 
      这就可以使用线程来实现。 
      本文主要介绍关于Thread和ThreadPool的基础知识。

    Thread类

      基本用法

      使用Thread类可以创建和控制线程,在下面的示例代码中,Thread类的构造函数重载为接受ThreadStart和ParameterizedThreadStart类型的委托参数。ThreadStart委托定义了一个返回类型为void的无参数方法,在创建Thread对象后,就可以用Start()方法启动线程。

     1 using System;
     2 using System.Threading;
     3 
     4 namespace ThreadDemo
     5 {
     6     class Program
     7     {
     8         static void Main()
     9         {
    10             var t1 = new Thread(ThreadMain);
    11             t1.Start();
    12             Console.WriteLine("This is the main thread.");
    13         }
    14         static void ThreadMain()
    15         {
    16             Console.WriteLine("Running in a thread.");
    17         }
    18     }
    19 }

    程序执行结果,得到两个线程的输出: 
      This is the main thread. 
      Running in a thread. 
      我们知道,我们并不能保证那个结果先输出,这由操作系统调度决定。

      在前文中,我们探讨了将Lambda表达式和异步委托结合使用,这里我们也使用Lambda表达式与来给Thread类构造函数传递参数: 

     1 using System.Threading;
     2 
     3 namespace ThreadDemo
     4 {
     5     class Program
     6     {
     7         static void Main()
     8         {
     9             var t1 = new Thread(()=>Console.WriteLine("Running in a thread, id : {0}",Thread.CurrentThread.ManagedThreadId));
    10             t1.Start();
    11             Console.WriteLine("This is the main thread, id : {0}",Thread.CurrentThread.ManagedThreadId);
    12         }
    13     }
    14 }

    在应用程序的输出中,我们可以看到线程id,当然,每次运行的结果不一定一样,因为系统每次分配的线程是独立的。 
      This is the main thread, id : 1. 
      Running in a thread, id : 3.

      给线程传递数据

      前面的例子中,我们开启的新线程只是执行了一个简单的输出指令,并没有线程中的方法赋予参数。现在我们来探讨如何给线程传递参数,也就是传递数据。 
      一种方式是使用带ParameterizedThreadStart委托参数的Thread构造函数; 
      另一种方法是将自定义的方法传递给线程,然后启动线程。
     
       
      ● 方法一:使用ParameterizedThreadStart委托。 
      该委托的实例方法必须带有一个object参数,而且返回类型为void。 
      假设该委托实例方法如下:

    1 static void ThreadMainWithParameters(object o)
    2 {
    3     ...
    4     ...
    5 }

    那么我可以这样开启线程:

    1 var o = new object();
    2 var t1 = new Thread(ThreadMainWithParameters);
    3 t1.Start(o);

    方法二:使用自定义方法。 
      假设我们有一个MyThread类,该类有一个方法ThreaMain():

     1 public class MyThread
     2 {
     3     ...
     4     ...
     5     public void ThreadMain()
     6     {
     7         ...
     8         ...
     9     }
    10     ...
    11     ...
    12 }

    开启线程方法如下:

    1 var o = new MyThread();
    2 var t1 =new Thread(o.ThreadMain);
    3 t1.Start();

    线程池

      我们知道,线程的创建需要时间。 如果有不同的小任务要完成,我们就可以事先创建许多线程, 在应完成这些任务时发出请求。 这个线程数最好在需要更多的线程时增加,在需要释放资源时减少。 
      这些线程就是放在线程池中,C#为我们提供了一个管理线程池的类:ThreadPool。 
      它会在需要的时候增减线程池中的线程数,如果线程池中线程数到达上限,新的作业就需要排队等待其他线程完成其任务。 
       
      下面的示例应用程序首先要读取工作线程和 I/O线程的最大线程数,把这些信息写入控制台中。接着在for循环中,调用 ThreadPool.QueueUserWorkItem()方法,传递一个WaitCallBack类型的委托,把 JobForThread()方法赋予线程池中的线程。线程池收到这个请求后,就会从池中选择一个线程,来调用该方法。 如果线程池还没有运行,就会创建一个线程池,并启动第一个线程。 如果线程池己经在运行,且有一个空闲线程来完成该任务,就把该作业传递给这个线程。 

     1 using System;
     2 using System.Threading;
     3 
     4 namespace ThreadDemo
     5 {
     6     class program
     7     {
     8         static void Main()
     9         {
    10             int nWorkThreads;
    11             int nCompletionPortThreads;
    12             ThreadPool.GetMaxThreads(out nWorkThreads, out nCompletionPortThreads);
    13             Console.WriteLine("Max worker threads: {0}, I/O completion threads: {1}",nWorkThreads, nCompletionProtThreads);
    14             for(int i = 0; i < 5; i++)
    15             {
    16                 ThreadPool.QueueUserWorkItem(JobForAThread);
    17             }
    18             Thread.Sleep(3000);
    19         }
    20 
    21         static void JobForAThread(object state)
    22         {
    23             for(int i = 0; i < 3; i++)
    24             {
    25                 Console.WriteLine("loop {0}, running inside pooled thread {1}", i, Thread.CurrentThread.ManagedThreadId);
    26                 Thread.Sleep(50);
    27             }
    28         }
    29     }
    30 }

    读者运行该程序的结果可能与此不同,也可以改变作业的睡眠时间和要处理的作业数,得到完全不同的结果。 
      这里写图片描述

      线程池使用起来很简单,但它有一些限制 : 
      ● 线程池中的所有线程都是后台线程 。 如果进程的所有前台线程都结束了,所有的后台线程就会停止。 不能把入池的线程改为前台线程 。 
      ● 不能给入池的线程设置优先级或名称。 
      ● 对于 COM对 象,入池的所有线程都是多线程单元(multit-threaded apartment , MTA)线程。 许 COM对象都需要单线程单元(single-threaded apartment , STA)线 程。 
      ● 入池的线程只能用于时间较短的任务。 如果线程要一直运行(如Word的拼写检查器线程),就应使用Thread类创建一个线程。

    转载来源:http://blog.csdn.net/honantic/article/details/46884537

  • 相关阅读:
    linux软件安装方式
    docker 安装 jenkins touch: cannot touch ‘/var/jenkins_home/copy_reference_file.log’: Permission denied Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
    [ERR] Node goodsleep.vip:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
    Linux 常用命令 服务器间scp 用户 export 创建文件、软连接
    redis 安装 集群 主从 哨兵 docker
    WPF密码框中禁止复制、粘贴
    Application 统计在线人数
    【转义字符】HTML 字符实体&lt; &gt: &amp;等
    SQL语句统计每天的数据
    正则表达式计算代码数
  • 原文地址:https://www.cnblogs.com/swfpt/p/6847064.html
Copyright © 2011-2022 走看看