多线程基础-C#

多线程


  • 线程 被定义为程序的执行流。每个线程都定义了一个单独的控制流。如果应用程序涉及到复杂的和耗时的操作,那么将程序分成多个线程执行流往往是有益的,每个线程执行特定的工作。
  • 线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率。
  • 为了同时执行多个任务,一个程序可以被划分为更小的线程。

线程生命周期

线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。

  • 未启动状态:当线程实例被创建但Start方法未被调用时的状况。

  • 就绪状态:当调用了Start,线程准备好运行并等待 CPU 周期时的状况。

  • 不可运行状态:下面的几种情况下线程是不可运行的:

  • 已经调用 Sleep 方法

  • 已经调用 Wait 方法

  • 通过 I/O 操作阻塞

  • 死亡状态:当线程已完成执行或已中止时的状况。

主线程

  • 在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程。进程中第一个被执行的线程称为主线程。
  • 当 C# 程序开始执行时,主线程自动创建。使用 Thread 类创建的线程被主线程的子线程调用。您可以使用 Thread 类的 CurrentThread 属性访问线程。

创建线程

构造函数

#ThreadStart:
#public delegate void ThreadStart();//一个无参数,无返回值的委托

#ParameterizedThreadStart:
#public delegate void ParameterizedThreadStart(object obj);//一个有一个参数、但无返回值的委托

#maxStackSize:线程要使用的最大堆栈大小(以字节为单位);如果为 0 则使用可执行文件的文件头中指定的默认最大堆栈大小。 重要地,对于部分受信任的代码,如果 maxStackSize 大于默认堆栈大小,则将其忽略。 不引发异常。
public Thread(ThreadStart start);
public Thread(ParameterizedThreadStart start);
public Thread(ThreadStart start, int maxStackSize);
public Thread(ParameterizedThreadStart start, int maxStackSize);

新建线程

public static void CallToChildThread()
{
   Console.WriteLine("Child thread starts");
}

ThreadStart childref = new ThreadStart(CallToChildThread);
Thread childThread = new Thread(childref);
childThread.Start();

调用Start之后,一个线程就进入了就绪状态了,等着CPU分配时间片即可。

以上,就是C#的多线程基本知识,如果你只是需要偶尔在程序中使用以下多线程,那么上面的知识就够你启动一个子线程,并将想要执行的任务放到子线程中执行了。

但如果你在线程中大量使用多线程,那么就可能遇到诸如:线程同步死锁控制线程状态等需求。


前后线程&后台线程

#获取或设置一个值,该值指示某个线程是否为后台线程。
public bool IsBackground { get; set; }

.Net的公用语言运行时(Common Language Runtime,CLR)能区分两种不同类型的线程:前台线程和后台线程。
这两者的区别就是:

  • 应用程序必须运行完所有的前台线程才可以退出;
  • 对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。

.NET环境使用Thread建立的线程默认情况下是前台线程,即线程属性IsBackground=false,在进程中,只要有一个前台线程未退出,进程就不会终止。主线程就是一个前台线程。而后台线程不管线程是否结束,只要所有的前台线程都退出(包括正常退出和异常退出)后,进程就会自动终止。

一般后台线程用于处理时间较短的任务,如在一个Web服务器中可以利用后台线程来处理客户端发过来的请求信息。而前台线程一般用于处理需要长时间等待的任务,如在Web服务器中的监听客户端请求的程序,或是定时对某些系统资源进行扫描的程序。

  • 应用程序的主线程以及使用Thread构造的线程都默认为前台线程
  • 线程池线程也就是使用 ThreadPool.QueueUserWorkItem()和Task工厂创建的线程都默认为后台线程

线程执行状态(IsAlive)

#获取一个值,该值指示当前线程的执行状态。
#如果此线程已启动并且尚未正常终止或中止,则为 true;否则为 false。
public bool IsAlive { get; }

线程状态(ThreadState)

#获取一个值,该值包含当前线程的状态
#System.Threading.ThreadState 值之一(Running、StopRequested、SuspendRequested、Background、Unstarted、Stopped、WaitSleepJoin、Suspended、AbortRequested、Aborted),它指示当前线程的状态。 初始值为 Unstarted。
public ThreadState ThreadState { get; }

线程归属(IsThreadPoolThread)

#获取一个值,该值指示线程是否属于托管线程池。
#如果此线程属于托管线程池,则为 true;否则为 false。
public bool IsThreadPoolThread { get; }

线程标识符(ManagedThreadId)

#获取当前托管线程的唯一标识符。
#一个整数,表示此托管线程的唯一标识符。
public int ManagedThreadId { get; }

线程名字(Name)

#获取或设置线程的名称。
#包含线程名称的字符串,或者如果未设置名称,则为 null。
public string Name { get; set; }

线程优先级(Priority)

#获取或设置一个值,该值指示线程的调度优先级。
#System.Threading.ThreadPriority(Lowest、BelowNormal、Normal、AboveNormal、Highest) 值之一。 默认值为 Normal。
public ThreadPriority Priority { get; set; }

立即执行当前线程,同时阻塞其余线程

#在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止或经过了指定时间为止。
#如果线程已终止,则为 true;如果线程在经过了 millisecondsTimeout 参数指定的时间量后未终止,则为 false。
public void Join();
public bool Join(int millisecondsTimeout);
public bool Join(TimeSpan timeout);

这个方法可以使当前线程立即执行,如果没有参数,则会一直等到当前线程执行结束,如果有参数,则当过时间之后,就会取消这种超级优先级状态。

挂起线程与继续挂起线程

#挂起线程,或者如果线程已挂起,则不起作用。
public void Suspend();
#继续已挂起的线程。
public void Resume();

线程休眠(sleep)

#timeout:设置为线程被阻塞的时间量的 System.TimeSpan。 指定零以指示应挂起此线程以使其他等待线程能够执行。 指定 System.Threading.Timeout.Infinite以无限期阻止线程。
public static void Sleep(TimeSpan timeout);

#线程被阻塞的毫秒数。 指定零 (0) 以指示应挂起此线程以使其他等待线程能够执行。 指定 System.Threading.Timeout.Infinite以无限期阻止线程。
public static void Sleep(int millisecondsTimeout);
#当前线程暂停 5000 毫秒
int sleepfor = 5000;
Thread.Sleep(sleepfor);

销毁线程(Abort)

  • Abort() 方法用于销毁线程。
  • 通过抛出 threadabortexception 在运行时中止线程。这个异常不能被捕获,如果有 finally 块,控制会被送至 finally 块。
#在调用此方法的线程上引发 System.Threading.ThreadAbortException,以开始终止此线程的过程。 调用此方法通常会终止线程。
public void Abort(object stateInfo);
public void Abort();
public static void CallToChildThread()
{
     try{
           Console.WriteLine("Child thread starts");
           // 计数到 10
           for (int counter = 0; counter <= 10; counter++)
           {
               Thread.Sleep(500);
               Console.WriteLine(counter);
           }
           Console.WriteLine("Child Thread Completed");
       }
       catch (ThreadAbortException e)
       {
           Console.WriteLine("Thread Abort Exception");
       }
       finally
       {
           Console.WriteLine("Couldn't catch the Thread Exception");
       }
}

static void Main(string[] args)
{
    ThreadStart childref = new ThreadStart(CallToChildThread);
    Console.WriteLine("In Main: Creating the Child thread");
    Thread childThread = new Thread(childref);
    childThread.Start();
    // 停止主线程一段时间
    Thread.Sleep(2000);
    // 现在中止子线程
    Console.WriteLine("In Main: Aborting the Child thread");
    childThread.Abort();
    Console.ReadKey();  
}
        

当上面的代码被编译和执行时,它会产生下列结果:

In Main: Creating the Child thread
Child thread starts
0
1
2
In Main: Aborting the Child thread
Thread Abort Exception
Couldn't catch the Thread Exceptio
多线程基础-C#_第1张图片
一个每日更新的干货公众号

你可能感兴趣的:(多线程基础-C#)