【问题标题】:Windows Critical Section strange behaviourWindows 关键部分奇怪的行为
【发布时间】:2011-01-19 10:03:54
【问题描述】:

我有两个共享的全局变量

int a = 0;
int b = 0;

还有两个线程

// thread 1
for (int i = 0; i < 10; ++i) {
    EnterCriticalSection(&sect);
    a++;
    b++;
    std::cout << a " " << b << std::endl;
    LeaveCriticalSection(&sect);
}

// thread2
for (int i = 0; i < 10; ++i) {
    EnterCriticalSection(&sect);
    a--;
    b--;
    std::cout << a " " << b << std::endl;
    LeaveCriticalSection(&sect);
}

代码总是打印以下输出

1 1

2 2

3 3

4 4

5 5

6 6

7 7

8 8

9 9

10 10

9 9

8 8

7 7

6 6

5 5

4 4

3 3

2 2

1 1

0 0

这很奇怪,看起来线程是按顺序工作的……这是什么问题?

谢谢。

【问题讨论】:

  • 你为什么觉得它很奇怪?当你启动第二个线程时,第一个线程可能已经完成了它的工作。
  • 是的,实际上是有可能的,但是同样的输出也包含大量的数据,并且对于另一个互斥对象(例如 std::mutex),它的工作方式就像预期的那样。
  • 刚刚测试了 10000 个增量/减量。是的,你是对的,这就是原因,但无论如何它的工作原理很奇怪,看起来它没有给两个线程大约相等的时间..差异很大,我可以看到它增加值超过一秒钟..
  • 顺便说一句,如果您使用 C++(如标签所示),最好将CCriticalSectionCSingleLock 一起使用,这样您就有了异常安全性。
  • @Naveen:并非所有 C++ 代码都使用 MFC(感谢上帝!)。 :)

标签: c++ winapi mutex critical-section


【解决方案1】:

每个线程都有一个特定的时间片,在此期间它会在被抢占之前执行。在您的示例中,时间片似乎比完成循环所需的时间长。

但是,您可以在将关键部分留在循环中后调用Sleep(0) 主动让出控制权。

【讨论】:

  • 我使用的是 2 核计算机,每个线程都真正并行运行,这不是上下文切换问题。
  • 嗯,是的。即使在两个内核上,一个线程也可以在另一个线程启动之前完成。此外,系统可能会或可能不会将两个线程调度在同一个核心上。请记住,还有数百个来自其他进程的其他线程......
  • 好吧,也许它应该是这样工作的。你真的不能说,系统做了这么多事情,我只是想确保不是Jiri提到的问题。临界区是否满足有界等待标准?
  • 别担心。当您离开临界区时,在任何线程上都可以进入它。然后一个刚刚离开它的人将不会以任何方式受到系统的青睐(实际上,调用LeaveCriticalSection() 并没有那么快——毕竟,这是一个系统调用)。但是离开临界区也不是上下文切换的原因。您是否真的在离开后尝试添加 Sleep(0) ?结果如何?
  • Sleep(0) 不是正确的屈服方式。 blogs.msdn.com/b/oldnewthing/archive/2005/10/04/476847.aspx
【解决方案2】:

您的示例中的 IMO 关键部分离开/进入非常快,以至于另一个线程不够快在此期间执行进入部分。

尝试放置一些(可能是随机的)睡眠以减慢代码速度以查看所需的效果。

注意: EnterCriticalSection 的默认超时时间为 30 天左右(意味着无穷大),因此您不能指望该函数会超时。 documentation 说:

无法保证线程获得临界区所有权的顺序,但是,系统对所有线程都是公平的。

【讨论】:

  • 我也是这么想的,这正是我关心的问题。如果是这样,那么临界区不满足互斥基本标准 - 有限等待。
  • 我认为它的行为完全正确 - IMO 关键部分是为了确保正确访问“共享资源”。想象一下,你有一堆不同优先级的线程,它们正在争夺资源。您可能需要某种队列来解决您的(未完全描述的)问题。
  • 我刚刚读到了有限等待 - 是的,你可能是对的。这似乎不符合这个标准。
  • @ledokol 在文档中哪里说临界区满足有限等待?我还找不到那部分文档,但 MSDN 是出了名的难搜索。
  • 我很确定在 Vista 中,关键部分是公平的。如果临界区没有争用,则不会释放当前线程量,线程将继续运行。
【解决方案3】:

对我来说,这看起来像是http://social.msdn.microsoft.com/forums/en-US/windowssdk/thread/980e5018-3ade-4823-a6dc-5ddbcc3091d5/ 中讨论的主题 请看 2006 年 6 月 28 日的示例

(很遗憾我找不到微软关于CriticalSection更改的原始文章)

您能在 Windows XP 上试用您的代码吗?它显示了什么?

猜测 I/O 操作 (cout) 会影响调度,类似于 Sleep() 调用,因此从 Windows Vista 开始,当在内部执行 I/O 时,线程可能会导致其他线程饥饿一个 CS。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    相关资源
    最近更新 更多