.Net 线程中的关键区域未按预期工作
我正在尝试运行涉及线程和关键区域的示例代码(非常基本的代码)。
这是我的代码:
public static void DoCriticalWork(object o)
{
SomeClass instance = o as SomeClass;
Thread.BeginCriticalRegion();
instance.IsValid = true;
Thread.Sleep(2);
instance.IsComplete = true;
Thread.EndCriticalRegion();
instance.Print();
}
我按如下方式调用它:
private static void CriticalHandled()
{
SomeClass instance = new SomeClass();
ParameterizedThreadStart operation = new ParameterizedThreadStart(CriticalRegion.DoCriticalWork);
Thread t = new Thread(operation);
Console.WriteLine("Start thread");
t.Start(instance);
Thread.Sleep(1);
Console.WriteLine("Abort thread");
t.Abort();
Console.WriteLine("In main");
instance.Print();
}
但是,我得到的输出是:
**
Start thread
Abort thread
In main
IsValid: True
IsComplete: False
**
由于定义了临界区域,IsComplete 应该为 true 而不是 false。
有人可以解释一下为什么它不起作用吗?
以下是 SomeClass 供参考:
public class SomeClass
{
private bool _isValid;
public bool IsValid
{
get { return _isValid; }
set { _isValid = value; }
}
private bool _isComplete;
public bool IsComplete
{
get { return _isComplete; }
set { _isComplete = value; }
}
public void Print()
{
Console.WriteLine("IsValid: {0}", IsValid);
Console.WriteLine("IsComplete: {0}", IsComplete);
Console.WriteLine();
}
}
Edit
Expln from MCTS Notes: 关键区域背后的想法是提供一个必须像单行一样执行的代码区域。当线程位于临界区域内时,任何中止线程的尝试都必须等到临界区域完成之后。此时,线程将被中止,并抛出 ThreadAbortException。有和没有临界区的线程的区别如下图所示:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Thread.BeginCriticalRegion 不会阻止线程中止。我相信它是用来通知运行时,如果线程在关键部分中止,继续运行应用程序/AppDomain不一定安全。
MSDN 文档有更完整的解释: http:// msdn.microsoft.com/en-us/library/system.threading.thread.beginriticregion.aspx
Thread.BeginCriticalRegion does not prevent a Thread from being aborted. I believe it is used to notify the runtime that if the Thread is aborted in the critical section, it is not necessarily safe to continue running the application/AppDomain.
The MSDN docs have a more complete explanation: http://msdn.microsoft.com/en-us/library/system.threading.thread.begincriticalregion.aspx
这是一个睡眠时间问题。只要拉大两次睡眠之间的差距,你就会得到答案。
有两个线程:主线程和执行关键工作的线程。现在,当调用 abort 时,线程“t”将立即中止,即使它尚未完成临界区域。
现在,当您让主线程休眠 2 毫秒,线程 t 休眠 1 毫秒时,有时 t 会完成关键部分,有时则不会。这就是为什么 IsComplete 的值有时为假、有时为真的原因。
现在只需让主线程休眠 100 毫秒,您就会发现 IsComplete 始终为 true。
反之亦然,让线程“t”休眠 100 毫秒,你会发现 IsComplete 始终为 false。
编辑
来自 MSDN
通知主机执行即将进入一个代码区域,在该区域中线程中止或未处理的异常的影响可能会危及应用程序域中的其他任务。
例如,考虑一个在持有锁的情况下尝试分配内存的任务。如果内存分配失败,中止当前任务不足以确保 AppDomain 的稳定性,因为域中可能有其他任务在等待同一锁。如果当前任务终止,其他任务可能会陷入死锁。
当关键区域发生故障时,主机可能决定卸载整个 AppDomain,而不是冒着在潜在不稳定状态下继续执行的风险。要通知主机您的代码正在进入关键区域,请调用 BeginCriticalRegion。当执行返回到代码的非关键区域时调用 EndCriticalRegion。
来自CLR 全面深入:编写可靠的代码
状态损坏
国家腐败可能分为三类。第一个是本地状态,其中包括仅由特定线程使用的本地变量和堆对象。第二个是共享状态,其中包括 AppDomain 内线程之间共享的任何内容,例如存储在静态变量中的对象。缓存通常属于这一类。第三个是进程范围、机器范围和跨机器共享状态——文件、套接字、共享内存和分布式锁管理器都属于这个阵营。
异步异常可能破坏的状态量是线程当前正在修改的最大状态量。如果一个线程分配了一些临时对象并且不将它们公开给其他线程,则只有这些临时对象可能会被损坏。 但是,如果线程正在写入共享状态,则该共享资源可能会损坏,并且其他线程可能会遇到此损坏的状态。你一定不能让这种事发生。在这种情况下,您将中止 AppDomain 中的所有其他线程,然后卸载 AppDomain。通过这种方式,异步异常会升级到 AppDomain,导致其卸载并确保丢弃任何可能损坏的状态。给定像数据库这样的事务存储,这种 AppDomain 回收提供了对本地和共享状态损坏的弹性。
关键区域允许您处理某些代码可能会破坏其他应用程序域并对系统造成无法修复的损坏的情况。
Its a sleep timing problem. Just widen the gap between the two sleeps and you will get the answer.
There are two threads: The main thread and the thread that does the critical work. Now when abort is called the thread 't' will abort instantly even if it has not completed the critical region.
Now as you have send the main thread to sleep for 2ms and thread t for 1ms, sometimes t will complete the critical section and sometimes it will not. So that's why the value for IsComplete is sometimes false and sometimes true.
Now just send the main thread to sleep for 100ms and you will find the IsComplete is always true.
Vice versa send the thread "t" to sleep for 100ms and you will find the IsComplete is always false.
EDIT
FROM MSDN
Notifies a host that execution is about to enter a region of code in which the effects of a thread abort or unhandled exception might jeopardize other tasks in the application domain.
For example, consider a task that attempts to allocate memory while holding a lock. If the memory allocation fails, aborting the current task is not sufficient to ensure stability of the AppDomain, because there can be other tasks in the domain waiting for the same lock. If the current task is terminated, other tasks could be deadlocked.
When a failure occurs in a critical region, the host might decide to unload the entire AppDomain rather than take the risk of continuing execution in a potentially unstable state. To inform the host that your code is entering a critical region, call BeginCriticalRegion. Call EndCriticalRegion when execution returns to a non-critical region of code.
From CLR Inside Out: Writing Reliable Code
State Corruption
There are three buckets that state corruption may fall into. The first is local state, which includes local variables and heap objects that are only used by a particular thread. The second is shared state, which includes anything shared across threads within the AppDomain, such as objects stored in static variables. Caches often fall into this category. The third is process-wide, machine-wide, and cross-machine shared state—files, sockets, shared memory, and distributed lock managers fall into this camp.
The amount of state that can be corrupted by an async exception is the maximum amount of state a thread is currently modifying. If a thread allocates a few temporary objects and doesn't expose them to other threads, only those temporary objects can be corrupted. But if a thread is writing to shared state, that shared resource may be corrupted, and other threads may potentially encounter this corrupted state. You must not let that happen. In this case, you abort all the other threads in the AppDomain and then unload the AppDomain. In this way, an asynchronous exception escalates to an AppDomain, causing it to unload and ensuring that any potentially corrupted state is thrown away. Given a transacted store like a database, this AppDomain recycling provides resiliency to corruption of local and shared state.
Critical Regions allow you to handle situations where some piece of code might corrupt other Application Domains and causing irreparable damage to the system.
封装
DoCriticalWork()
中的代码,一个好的解决方案是使用
try { ... } catch(ThreadAbortedException) {...}
您应该按照自己的意愿行事(也许设置
IsComplete = true
?)您可以阅读有关ThreadAbortException,并确保检查
Thread.ResetAbort
和finally
块在这种情况下的使用情况。正如Andy提到并引用Thread.BeginCriticalRegion:
A good solution is to encapsulate the code from
DoCriticalWork()
withtry { ... } catch(ThreadAbortedException) {...}
where you should act as you wish (maybe set
IsComplete = true
?)You can read more about ThreadAbortException, and be sure to check
Thread.ResetAbort
and thefinally
block usage for this case.As Andy mentioned and quoting from Thread.BeginCriticalRegion: