多线程总结之旅:多线程的创建属性

Posted 搬砖滴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程总结之旅:多线程的创建属性相关的知识,希望对你有一定的参考价值。

可以参考微软文档:https://msdn.microsoft.com/en-us/library/system.threading.thread(v=vs.110).aspx

 

 

一、Thread  

  1、多线程执行无参数的方法:

  (1)线程用Thread类来创建, 通过ThreadStart委托来指明方法从哪里开始运行,调用Start方法后,线程开始运行,线程一直到它所调用的方法返回后结束。

 class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(new ThreadStart(Go));
            t.Start();
        }
        public static void Go()
        {
            Console.WriteLine("GO");           
        }
    }

  (2)当然也可以不需要ThreadStart委托更便利地创建出来:在这种情况,ThreadStart被编译器自动推断出来。

static void Main(string[] args)
        {
            Thread t = new Thread(Go);
            t.Start();
        }
        public static void Go()
        {
            Console.WriteLine("GO");           
        }

  (3)另一个快捷的方式是使用匿名方法来启动线程:

 static void Main(string[] args)
        {
            Thread t = new Thread(delegate() {Console.WriteLine("GO");   });
            t.Start();
        }

  2、多线程执行有参数的方法  

  (1)有时候我们需要给Go方法传参来完成整个任务,但我们不能使用ThreadStart委托,因为它不接受参数,所幸的是,.NET framework定义了另一个版本的委托叫做ParameterizedThreadStart, 它可以接收一个单独的object类型参数:

class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(Go);
            t.Start(true);
        }
        public static void Go(object j)
        {
            Console.WriteLine("GO");           
        }
    }

  (2)ParameterizedThreadStart的特性是在使用之前我们必需对我们想要的类型(这里是bool)进行装箱操作,并且它只能接收一个参数。

  一个替代方案是使用一个匿名方法调用一个普通的方法如下:

 static void Main(string[] args)
        {
            Thread t = new Thread(delegate() { Go("jack", "hello"); });
            t.Start();
            Console.ReadKey();
        }
        public static void Go(string name,string saying)
        {
            Console.WriteLine("{0},{1}",name,saying);           
        }

  (3)优点是目标方法(这里是Go),可以接收任意数量的参数,并且没有装箱操作。不过这需要将一个外部变量放入到匿名方法中,匿名方法打开了一种怪异的现象,当外部变量被后来的部分修改了值的时候,可能会透过外部变量进行无意的互动。有意的互动(通常通过字段)被认为是足够了!一旦线程开始运行了,外部变量最好被处理成只读的——除非有人愿意使用适当的锁。

static void Main(string[] args)
        {
            string test = "Tom";
            Thread t = new Thread(delegate() { Go(test, "hello"); });
            test = "Jack";
            t.Start();
            Console.ReadKey();
        }
        public static void Go(string name,string saying)
        {
            Console.WriteLine("{0},{1}",name,saying);           
        }

  (4)另一种较常见的方式是将对象实例的方法而不是静态方法传入到线程中,对象实例的属性可以告诉线程要做什么

class Program
    {
        bool lower = false;
        static void Main(string[] args)
        {
            Program test = new Program();
            test.lower = true;
            Thread t = new Thread(test.Go);          
            t.Start();
            Console.ReadKey();
        }
        void Go()
        {
            Console.WriteLine(lower?"Hello":"hello");           
        }
    }

二、Thread的属性

  1、静态属性和动态属性。静态属性包括CurrentThread ,CurrentContext,CurrentPrincipal(负责人);动态属性包括Priority,ThreadState ,IsAlive,IsBackground,IsThreadPoolThread,ManagedThreadId,ApartmentState,CurrentCulture,CurrentUICulture,ExecutionContext,Name

  (1)CurrentThread 和Name的使用

    线程可以通过它的Name属性进行命名,这非常有利于调试:可以用Console.WriteLine打印出线程的名字,Microsoft Visual Studio可以将线程的名字显示在调试工具栏的位置上。线程的名字可以在被任何时间设置——但只能设置一次,重命名会引发异常。

    CurrentThread  获取正在运行的线程。 

static void Main(string[] args)
        {
          
            Thread t = new Thread(Go);
            t.Name = "我是子线程";
            t.Start();
            Console.ReadKey();
        }
        static void Go()
        {
            string currentThread = Thread.CurrentThread.Name;
            Console.WriteLine(currentThread);           
        }

  (2)CurrentContext:获取正在执行线程的当前上下文(线程上下文是指:线程运行需要的环境,参数等条件)

 static void Main(string[] args)
        {
          
            Thread t1 = new Thread(Go);           
            t1.Start();                 
            Console.ReadKey();
            
        }
        static void Go()
        {            
            //获取当前上下文的上下文ID
            int currentContextId = Thread.CurrentContext.ContextID;
            Console.WriteLine(currentContextId);           
        }

  (3)CurrentPrincipal 获取或设置线程的当前负责人(对基于角色的安全性而言)

 

 static void Main(string[] args)
        {
          string[] rolesArray = {"managers", "executives"};
            try
            {
                // 设置当前负责人。从用户表示和角色名称数组初始化
                Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("Bob", "Passport"), rolesArray);
            }
            catch(SecurityException secureException)
            {
                Console.WriteLine("{0}: Permission to set Principal " +
                    "is denied.", secureException.GetType().Name);
            }

            IPrincipal threadPrincipal = Thread.CurrentPrincipal;
            Console.WriteLine("Name: {0}\n是否认证: {1}" +"\n认证的类别: {2}", 
                threadPrincipal.Identity.Name, 
                threadPrincipal.Identity.IsAuthenticated,
                threadPrincipal.Identity.AuthenticationType);
            Console.ReadKey() ;
        }

  (4)IsAlive  获取一个值,该值指示当前线程的执行状态。经测试只有 Unstarted、Stopped 返回false;其他线程状态都返回true。它的定义如下:只读

  public bool IsAlive { get; }

  (5)IsBackground 获取或设置一个值,该值指示某个线程是否为后台线程。它的定义如下:可以读写操作。    

public bool IsBackground {
    get;
    [HostProtectionAttribute(SecurityAction.LinkDemand, SelfAffectingThreading = true)]
    set;
}

    线程默认为前台线程,这意味着任何前台线程在运行都会保持程序存活。C#也支持后台线程,当所有前台线程结束后,它们不维持程序的存活。 改变线程从前台到后台不会以任何方式改变它在CPU协调程序中的优先级和状态。线程的IsBackground属性控制它的前后台状态,如下实例:

 

class PriorityTest {
  static void Main (string[] args) {
    Thread worker = new Thread (delegate() { Console.ReadLine(); });
    if (args.Length > 0) worker.IsBackground = true;
    worker.Start();
  }
}

  (6)Priority 获取或设置一个值,该值指示线程的调度优先级。

    优先级别有以下五种:Highest、AboveNormal、Normal、BelowNormal、Lowest。默认为Normal.只有多个线程同时为活动时,优先级才有作用。  设置一个线程的优先级为高一些,并不意味着它能执行实时的工作,因为它受限于程序的进程的级别。要执行实时的工作,必须提升在System.Diagnostics 命名空间下Process的级别,像下面这样:Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

 

     ProcessPriorityClass.High 其实是一个短暂缺口的过程中的最高优先级别:Realtime。设置进程级别到Realtime通知操作系统:你不想让你的进程被抢占了。如果你的程序进入一个偶然的死循环,可以预期,操作系统被锁住了,除了关机没有什么可以拯救你了!基于此,High大体上被认为最高的有用进程级别。

  

  (7)IsThreadPoolThread  获取一个值,该值指示线程是否属于托管线程池

 

using System;
using System.Threading;

class IsThreadPool
{
    static void Main()
    {
        AutoResetEvent autoEvent = new AutoResetEvent(false);

        Thread regularThread = new Thread(new ThreadStart(ThreadMethod));
        regularThread.Start();
        ThreadPool.QueueUserWorkItem(new WaitCallback(WorkMethod),autoEvent);

        // Wait for foreground thread to end.
        regularThread.Join();

        // Wait for background thread to end.
        autoEvent.WaitOne();
        Console.ReadKey();
    }

    static void ThreadMethod()
    {
        Console.WriteLine("ThreadOne, executing ThreadMethod, " + "is {0}from the thread pool.",Thread.CurrentThread.IsThreadPoolThread ? "" : "not ");
    }

    static void WorkMethod(object stateInfo)
    {
        Console.WriteLine("ThreadTwo, executing WorkMethod, " + "is {0}from the thread pool.",Thread.CurrentThread.IsThreadPoolThread ? "" : "not ");

        // Signal that this thread is finished.
        ((AutoResetEvent)stateInfo).Set();
    }
}

  (8)ManagedThreadId 获取当前托管线程的惟一标识符:它的定义如下:只读

public int ManagedThreadId { get; }

 

  (9)ApartmentState 获取或设置此线程的单元状态

using System;
using System.Threading;

class ApartmentTest
{
    static void Main()
    {
        Thread newThread =new Thread(new ThreadStart(ThreadMethod));
        newThread.SetApartmentState(ApartmentState.MTA);
        Console.WriteLine("ThreadState: {0}, ApartmentState: {1}",newThread.ThreadState, newThread.ApartmentState);
        newThread.Start();

        // Wait for newThread to start and go to sleep.
        Thread.Sleep(300);
        try
        {
            // This causes an exception since newThread is sleeping.
            newThread.SetApartmentState(ApartmentState.STA);
        }
        catch (ThreadStateException stateException)
        {
            Console.WriteLine("\n{0} caught:\n" +"Thread is not in the Unstarted or Running state.",stateException.GetType().Name);
            Console.WriteLine("ThreadState: {0}, ApartmentState: {1}",newThread.ThreadState, newThread.GetApartmentState());
        }
        Console.ReadKey();
    }

    static void ThreadMethod()
    {
        Thread.Sleep(1000);
    }
}

 

  (10)CurrentCulture 获取或设置当前线程的区域性

using System;
using System.Threading;
using System.Windows.Forms;

class UICulture : Form
{
    public UICulture()
    {
        // Set the user interface to display in the
        // same culture as that set in Control Panel.
        Thread.CurrentThread.CurrentUICulture =Thread.CurrentThread.CurrentCulture;

        // Add additional code.
    }

    static void Main()
    {
        Application.Run(new UICulture());
    }
}

 

  (11)CurrentUICulture 获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源

using System;
using System.Globalization;
using System.Threading;

public class Example
{
    public static void Main()
    {
        // Change the current culture if the language is not French.
        CultureInfo current = Thread.CurrentThread.CurrentUICulture;
        if (current.TwoLetterISOLanguageName != "fr")
        {
            CultureInfo newCulture = CultureInfo.CreateSpecificCulture("en-US");
            Thread.CurrentThread.CurrentUICulture = newCulture;
            // Make current UI culture consistent with current culture.
            Thread.CurrentThread.CurrentCulture = newCulture;
        }
        Console.WriteLine("The current UI culture is {0} [{1}]",
                          Thread.CurrentThread.CurrentUICulture.NativeName,
                          Thread.CurrentThread.CurrentUICulture.Name);
        Console.WriteLine("The current culture is {0} [{1}]",
                          Thread.CurrentThread.CurrentUICulture.NativeName,
                          Thread.CurrentThread.CurrentUICulture.Name);
        Console.ReadKey();
    }
}

 

  (12)ExecutionContext 获取一个Executioncontext对象,该对象包含有关当前线程的各种上下文的信息

 

 

 

以上是关于多线程总结之旅:多线程的创建属性的主要内容,如果未能解决你的问题,请参考以下文章

干货分享:详解线程的开始和创建

多线程总结之旅:线程VS进程

多线程总结之旅(112):跨线程调用控件的几种方式

C#多线程之旅

C#多线程之旅——介绍和基本概念

C#多线程之旅