zoukankan      html  css  js  c++  java
  • 关于Thread的那些事


    关于Thread的那些事

    1 : 你能够调用线程的实例方法Join来等待一个线程的结束.比如:

    public static void MainThread()
            {
                Thread t = new Thread(Go);
                t.Start();
                t.Join();
                Console.WriteLine("Thread t has ended!");
            }
     
            static void Go()
            {
                for (int i = 0; i < 1000; i++) 
                    Console.Write("y");
            }
     


    分析:在打印了1000Y之后,后面就会输出”Thread has ended!”.

    你能够在调用Join方法的时候给它一个timeout的參数,比如要超过1.

             

       t.Join(1000);
                t.Join(TimeSpan.FromSeconds(1));
     

    2 : 为线程传递參数

    为线程传递參数的最简单的方法莫过于运行一个lambda表达式,然后在方法里面给參数了,比如:

            static void Main(string[] args)
            {
                Thread t = new Thread(()=>Print("hello ,world !"));
                t.Start();                   
                Console.ReadKey();
            }
            static void Print(string message)
            {
                Console.WriteLine(message);
            }


    使用这样的方法,你能够传递不论什么參数.

    当然Thread的构造函数中有一个传递參数的版本号,你也能够使用以下的代码来传递參数:

     static void Main()
            {
                Thread t = new Thread(Print);
                t.Start("Hello from t!");
            }
     
            static void Print(object messageObj)
            {
                string message = (string)messageObj;
                Console.WriteLine(message);
            }


    分析:这里有一点须要注意,由于Print的方法签名必须匹配ParameterrizedThreadStart托付,所以Print的參数必须也是object,所以在Print方法中必须进行强制转换.

    3: Lambda和捕获的变量

    考虑以下的代码

                for (int i = 0; i < 10; i++)
                {                
                    new Thread(() => Console.Write(i)).Start();
                }
     


    分析:实际的输出是不确定的,你能够自己试试.

    Why?

    关键问题是局部变量ifor循环中指向的是相同的内存地址.因此,每一次都在一个执行时会被改变值的变量(i)上调用CW方法,foreach中也存在相同问题.

    解决问题的方法非常easy,比如使用一个暂时变量:

                for (int i = 0; i < 10; i++)
                {
                    int temp = i;
                    new Thread(() => Console.Write(temp)).Start();
                }


    由于i是值变量,所以int temp=i会复制i的值给temp,而在for循环中temp变量都是不同的,所以能够解决问题.

    以下的也是相同的道理:

                for (int i = 0; i < 10; i++)
                {
                    new Thread((obj) => Console.Write(obj)).Start(i); //由于每个线程的obj都是不同的。
                }


    以下的案例可能更加明显一点:

                string text = "t1";
                Thread t1 = new Thread(() => Console.WriteLine(text));
                text = "t2";
                Thread t2 = new Thread(() => Console.WriteLine(text));
     
                t1.Start();
                t2.Start();
     


    无论你运行几次,貌似都是输出两次t2,为啥呢?

    由于两个lambda表达式捕获的是同样的test变量,所以”t2”会被连续打印两次.

    4.命名线程

    给每个线程一个合适的名字对于调试来说非常有利,尤其是在VS,由于县城窗体和调试位置工具栏中都会显示线程的名字.

    可是你仅仅能设置一次线程的名字,尝试在以后更改名字会抛出一个异常,为变量命名的使用的是Name属性,比如:

                Thread worker = new Thread(Go);
                worker.Name = "worker";
                worker.Name = "worker";//会抛出异常


    5.前台线程和后台线程

    默认你显示创建的线程都是前台线程.

    仅仅要前台线程有一个还在执行,应用程序就不会结束.

    仅仅有全部的前台线程都结束了,应用程序才会结束.

    在应用程序结束的时候,全部后台线程都会被终止.

    你能够通过现成的IsBackground属性莱产讯和更改现成的状态.

    案例:

                if (args.Length>0)
                {
                    worker.IsBackground = true;                
                }
                worker.Start();
     


    假设args.Length>0,worker就是后台线程,那么应用程序会马上终止.

    否则worker默认就是前台线程,所以仅仅有在CR()方法结束后,应用程序才会终止.

    所以当你有时候发现关闭了应用程序窗体,可是在任务管理器中应用程序仍然在执行,非常有可能是另一些前台线程在执行.

    6.线程的优先级

    enum ThreadPriority
    {
    Lowest,
    BelowNormal,
    Normal,
    AboveNormal,
    Highest
    }


    仅仅有在多个线程的环境中,线程优先级才实用.

    把一个现成的优先级提高并不会提高实时系统的性能,由于进程的优先级才是应用程序的瓶颈.为了更好的实时工作,你必须提高进程的优先级.

    比如:

    process.PriorityClass=ProcessPriorityClass.High.

     
    7.异常捕获

    尝试在一个线程中捕获还有一个线程的异常是失败的.案例:

    public static void Main()
            {
                try
                {
                    new Thread(Go).Start();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message); //永远执行不到这里.
                }
            }
     
            static void Go() { throw null;}
     


    我们再还有一个线程中抛出了异常(throw null),然后尝试在主线程中捕获它,我们永远都不会捕获到这个异常.

    为了在还有一个线程中捕获异常,必须在那个线程上try,catch,finally.

    所以我们能够这么做:

    public static void Main()
            {
                new Thread(Go).Start();
            }
     
            static void Go()
            {
                try
                {
                    throw null;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
     
     


    8.全局捕获异常

    Application.DispatcherUnhandledException 事件和Application.ThreadException 事件都仅仅有在主UI线程中抛出异常的时候才会被触发。

    为了捕获全部的未处理的异常,你能够使用AppDomain.CurrentDomain.UnhandledException,尽管这个事件在不论什么未处理异常抛出的时候都会被触发。可是它不能让你阻止应用程序的关闭。

  • 相关阅读:
    机器学习入门之二:一个故事说明什么是机器学习(转载)
    机器学习入门之一:背景介绍(转载)
    python 读取CSV文件 中文乱码
    如何快速学习一门新技术(转载)
    Oracle12c多租户如何启动关闭CDB或PDB (PDB自动启动)
    oracle单实例12.2.0.1安装
    PRVF-0002 : could not retrieve local node name
    图形化升级单机oracle 11.2.0.4 到 12.2.0.1
    ORA-00845: MEMORY_TARGET not supported on this system
    行转列、列转行
  • 原文地址:https://www.cnblogs.com/mthoutai/p/6945384.html
Copyright © 2011-2022 走看看