【发布时间】:2011-04-06 07:52:06
【问题描述】:
几周前我问了以下问题。现在,在查看我的问题和所有答案时,一个非常重要的细节跳入了我的视野:在我的第二个代码示例中,DoTheCodeThatNeedsToRunAsynchronously() 不是在主(UI)线程中执行的吗?计时器不只是等待一秒钟然后将事件发布到主线程吗?这意味着需要异步运行的代码根本就不是异步运行的?!
原问题:
我最近多次遇到一个问题,并以不同的方式解决它,总是不确定它是否是线程安全的:我需要异步执行一段 C# 代码。 (编辑:我忘了说我使用的是 .NET 3.5!)
那段代码作用于由主线程代码提供的对象。 (编辑:假设对象本身是线程安全的。)我将向您展示我尝试过的两种方法(简化)并有这四个问题:
- 实现我想要的最佳方式是什么?是这两种方法中的一种还是另一种方法?
- 两种方式之一是不是线程安全的(我担心两者都...)吗?为什么?
- 第一种方法创建一个线程并在构造函数中将对象传递给它。这就是我应该传递对象的方式吗?
- 第二种方法使用不提供这种可能性的计时器,所以我只使用匿名委托中的局部变量。这是安全的,还是理论上有可能变量中的引用在委托代码评估之前发生变化? (每当使用匿名代表时,这是一个非常通用的问题)。在 Java 中,您必须将局部变量声明为 final(即一旦分配就不能更改)。在 C# 中没有这种可能性,是吗?
方法一:线程
new Thread(new ParameterizedThreadStart(
delegate(object parameter)
{
Thread.Sleep(1000); // wait a second (for a specific reason)
MyObject myObject = (MyObject)parameter;
DoTheCodeThatNeedsToRunAsynchronously();
myObject.ChangeSomeProperty();
})).Start(this.MyObject);
这种方法有一个问题:我的主线程可能会崩溃,但由于僵尸线程,进程仍然存在于内存中。
方法 2:计时器
MyObject myObject = this.MyObject;
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 1000;
timer.AutoReset = false; // i.e. only run the timer once.
timer.Elapsed += new System.Timers.ElapsedEventHandler(
delegate(object sender, System.Timers.ElapsedEventArgs e)
{
DoTheCodeThatNeedsToRunAsynchronously();
myObject.ChangeSomeProperty();
});
DoSomeStuff();
myObject = that.MyObject; // hypothetical second assignment.
局部变量myObject 就是我在问题4 中所说的。我添加了第二个赋值作为示例。想象一下计时器在第二次分配之后过去,委托代码会在this.MyObject 或that.MyObject 上运行吗?
【问题讨论】:
-
在方法 2 的示例代码中,局部变量“myObject”永远不会重新分配,因此它永远不会在您的计时器代码主体中具有意外值。
-
@Kirk,很抱歉造成混乱。我添加了一行示例代码。错过它的原因是我实际上并没有在我的现实生活中重新分配它,但我想利用这个机会通过在我的示例中“嵌入”它来提出这个问题。
标签: c# multithreading .net-3.5 timer thread-safety