VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • C#线程 入门(2)

 

之所以可行,是因为Thread的构造函数被重载为接受两个委托之一:

1
2
public delegate void ThreadStart();
public delegate void ParameterizedThreadStart (object obj);

  

ParameterizedThreadStart的局限性在于它仅接受一个参数。而且由于它是object类型的,因此通常需要强制转换。

Lambda表达式和捕获的变量

如我们所见,lambda表达式是将数据传递到线程的最强大的方法。但是,您必须小心在启动线程后意外修改捕获的变量,因为这些变量是共享的。例如,考虑以下内容:
1
2
for (int i = 0; i < 10; i++)
  new Thread (() => Console.Write (i)).Start();

  输出是不确定的!这是一个典型的结果:

0223557799

问题在于,i变量在循环的整个生命周期中都指向相同的内存位置。因此,每个线程都会在变量上调用Console.Write,该变量的值可能会随着运行而改变!

 

这类似于我们在C#4.0的第八章“捕获变量”中描述的问题。问题不在于多线程,而是与C#捕获变量的规则有关(在for和foreach循环的情况下这是不希望的)。

 

 解决方案是使用如下临时变量:

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

  

现在,可变温度是每个循环迭代的局部变量。因此,每个线程捕获一个不同的内存位置,这没有问题。我们可以通过以下示例更简单地说明早期代码中的问题:

1
2
3
4
5
6
7
8
string text = "t1";
Thread t1 = new Thread ( () => Console.WriteLine (text) );
 
text = "t2";
Thread t2 = new Thread ( () => Console.WriteLine (text) );
 
t1.Start();
t2.Start();

  

因为两个lambda表达式都捕获相同的文本变量,所以t2被打印两次

t2
t2

命名线程

每个线程都有一个Name属性,可以设置该属性以利于调试。这在Visual Studio中特别有用,因为线程的名称显示在“线程窗口”和“调试位置”工具栏中。您只需设置一个线程名称即可;稍后尝试更改它会引发异常。

 

静态Thread.CurrentThread属性为您提供当前正在执行的线程。在以下示例中,我们设置主线程的名称:

复制代码
class ThreadNaming
{
  static void Main()
  {
    Thread.CurrentThread.Name = "main";
    Thread worker = new Thread (Go);
    worker.Name = "worker";
    worker.Start();
    Go();
  } static void Go()
  {
    Console.WriteLine ("Hello from " + Thread.CurrentThread.Name);
  }
}
复制代码

 

  

 
 

前台线程和后台线程

默认情况下,您显式创建的线程是前台线程。只要前台线程中的任何一个正在运行,它就可以使应用程序保持活动状态,而后台线程则不会。一旦所有前台线程完成,应用程序结束,所有仍在运行的后台线程终止。

 

线程的前台/后台状态与其优先级或执行时间的分配无关。

 

您可以使用其IsBackground属性查询或更改线程的背景状态。这是一个例子:

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

如果不带任何参数调用此程序,则工作线程将处于前台状态,并将在ReadLine语句上等待用户按Enter。同时,主线程退出,但是应用程序继续运行,因为前台线程仍然处于活动状态。

 

另一方面,如果将参数传递给Main(),则会为工作程序分配背景状态,并且在主线程结束(终止ReadLine)时,程序几乎立即退出。

 

当进程以这种方式终止时,将规避后台线程执行堆栈中的所有finally块。如果您的程序最终使用(或使用)块来执行清理工作(例如释放资源或删除临时文件),则会出现问题。为了避免这种情况,您可以在退出应用程序后显式等待此类后台线程。

有两种方法可以实现此目的:

 

在这两种情况下,您都应指定一个超时时间,以便在由于某种原因而拒绝完成的叛逆线程时可以放弃它。这是您的备份退出策略:最后,您希望您的应用程序关闭-无需用户从任务管理器中寻求帮助!

 

如果用户使用任务管理器强制结束.NET进程,则所有线程都“掉线”,就好像它们是后台线程一样。这是观察到的,而不是记录的行为,并且它可能因CLR和操作系统版本而异。


相关教程
关于我们--广告服务--免责声明--本站帮助-友情链接--版权声明--联系我们       黑ICP备07002182号