.NET中线程的关键区域未按预期工作

3

我将要运行一个简单的样例代码,涉及到线程和临界区。

以下是我的代码:

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();
        }
    }

编辑

MCTS笔记中的解释: 关键区域的概念是提供一个必须像单行代码一样执行的代码区域。任何试图在线程位于关键区域内时中止它的尝试都必须等待关键区域完成后才能进行。此时,线程将被中止并抛出ThreadAbortException。以下图示说明了具有和不具有关键区域的线程之间的区别:

关键区域 http://www.freeimagehosting.net/uploads/9dd3bb5445.gif


我刚刚运行了代码,它返回 IsComplete: True。 编辑:啊..现在是 false..似乎它在 true 和 false 之间切换。 - A G
3个回答

7

但是,Andy,我所参考的MCTS笔记指出,关键区域内的所有行都应被视为单个LOC,并且中止应等待关键部分的执行完成。我已经编辑了问题,包括MCTS笔记中相关的部分。 - Rashmi Pandit
@Rashmi Pandit:MCTS是错误的。防止代码在中止时被不完全执行的正确方法是https://dev59.com/tHI95IYBdhLWcg3wtwVe#2186123。 - kkm

1

这是一个睡眠时间的问题。只需扩大两个睡眠之间的间隔,您就会得到答案。

有两个线程:主线程和执行关键工作的线程。现在,当调用abort时,即使线程“t”尚未完成关键区域,它也会立即中止。

现在,由于您已经让主线程睡眠2ms,线程t睡眠1ms,有时t将完成关键部分,有时则不会。因此,IsComplete的值有时为false,有时为true。

现在只需让主线程睡眠100ms,您就会发现IsComplete始终为true。反之,让线程“t”睡眠100ms,您会发现IsComplete始终为false。

编辑

来自MSDN

通知主机即将进入代码区域,在该区域中,线程中止或未处理的异常可能会危及应用程序域中的其他任务。

例如,考虑一个在持有锁的情况下尝试分配内存的任务。如果内存分配失败,仅仅终止当前任务是不足以确保AppDomain的稳定性的,因为该域中可能还有其他等待相同锁的任务。如果终止当前任务,则可能会导致其他任务死锁。
当关键区域发生故障时,主机可能决定卸载整个AppDomain,而不是冒着在潜在不稳定状态下继续执行的风险。要通知主机您的代码正在进入关键区域,请调用BeginCriticalRegion。当执行返回到非关键代码区域时,请调用EndCriticalRegion。

来自CLR Inside Out:编写可靠代码

状态损坏 状态损坏可能分为三个桶。第一个是本地状态,包括仅由特定线程使用的局部变量和堆对象。第二个是共享状态,包括在AppDomain内跨线程共享的任何内容,例如存储在静态变量中的对象。缓存通常属于此类别。第三个是进程范围、机器范围和跨机器共享状态 - 文件、套接字、共享内存和分布式锁管理器属于此类别。

异步异常可能会破坏的状态量是线程当前正在修改的最大状态量。如果一个线程分配了一些临时对象并且没有将它们暴露给其他线程,那么只有这些临时对象可能会被破坏。但是,如果一个线程正在写入共享状态,那么这个共享资源可能会被破坏,其他线程可能会遇到这个已经被破坏的状态。你不能让这种情况发生。在这种情况下,你需要中止应用程序域中的所有其他线程,然后卸载应用程序域。通过这种方式,异步异常会升级到应用程序域,导致其卸载并确保任何可能被破坏的状态都被丢弃。对于像数据库这样的事务存储,这种应用程序域回收提供了本地和共享状态损坏的弹性。
关键区域允许你处理某些代码可能会破坏其他应用程序域并对系统造成不可修复的损害的情况。

0
一个好的解决方案是将代码从DoCriticalWork()封装起来。

尝试 { ... } 捕获(ThreadAbortedException) {...}


你可以按照自己的意愿行事(也许设置IsComplete = true?)

你可以阅读更多关于ThreadAbortException的内容,并确保检查Thread.ResetAbortfinally块在这种情况下的使用。

正如Andy所提到的并引用自Thread.BeginCriticalRegion

通知主机执行即将进入代码区域,在该区域中线程中止或未处理异常的影响可能危及应用程序域中的其他任务。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接