关于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中也存在同样问题.

 

解决这个问题的方法很简单,例如使用一个临时变量:

            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,虽然这个事件在任何未处理异常抛出的时候都会被触发,但是它不能让你阻止应用程序的关闭。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值