1

我正在尝试学习线程和互斥锁是如何工作的,但是我现在遇到了一些困惑,我从官方 SFML 1.6 教程中获取了以下代码:

#include <SFML/System.hpp>
#include <iostream>

void ThreadFunction(void* UserData)
{
    // Print something...
    for (int i = 0; i < 10; ++i)
        std::cout << "I'm the thread number 1" << std::endl;
}

int main()
{
    // Create a thread with our function
    sf::Thread Thread(&ThreadFunction);

    // Start it !
    Thread.Launch();

    // Print something...
    for (int i = 0; i < 10; ++i)
        std::cout << "I'm the main thread" << std::endl;

    return EXIT_SUCCESS;
}

它说

因此,来自两个线程的文本将同时显示。

但是这并没有发生,它首先执行第一个线程然后执行第二个线程,它们不应该同时运行吗?我在 Windows XP SP3 上使用 Codeblocks IDE,运行 SFML 1.6。我做错了什么,还是我误解了它们的工作原理?从我的角度来看,线程应该同时执行,所以输出应该是这样的

“来自线程 1 的文本来自线程 2 的文本来自线程 1 的文本,依此类推”

在此处输入图像描述

4

1 回答 1

4

......他们不应该同时运行吗?

嗯,这取决于。

如果您有两个或更多核心,它们可能会同时运行。

即使您有可用的硬件,也取决于您的操作系统来决定如何安排您的线程:如果您想鼓励您的操作系统交错两个线程(如果没有更多工作就无法强制执行),请尝试添加sleepornanosleepyield调用您的循环(确切的原语将取决于您的平台)。


如果它可以帮助您建立关于内核如何以及为什么做出调度决策的直觉,请注意大多数 CPU 架构将保留大量状态(分支预测表、数据和指令缓存),这些状态非常适合优化单线程执行。

因此,让给定线程尽可能长时间地在给定内核上运行通常更有效,以最大限度地减少可避免的上下文切换、缓存未命中和错误预测的数量。

现在,时间片通常被用作在每个单独进程的最佳吞吐量与最佳延迟或对外部事件的响应之间的一种权衡。线程可能会阻塞(通过等待外部事件,例如用户输入或设备 I/O,因为它显式与另一个线程同步,或者显式休眠或让步),在这种情况下,另一个线程将被调度,而第一个线程不能取得进展,但否则它通常会运行,直到内核在其分配的时间片结束时抢占它。

当父线程创建子线程时,我不想猜测当前内核上哪个“更热”,因此让父线程完成其时间片(除非它阻塞)是合理的默认值。

子线程可能立即可运行,但如果它不抢占父线程,那么为什么它应该立即抢占不同核心上的线程也不是很明显。毕竟,它还是和父线程在同一个进程中,并且共享相同的内存、地址映射和其他资源:除非另一个核心完全空闲,否则调度子线程的最佳位置可能是与其父线程在同一个核心上,因为父级很有可能将这些共享资源保存在缓存中。

因此,您的线程没有交错的原因很可能是在进程退出之前既不会运行相当一部分时间片,也没有任何阻塞 I/O 或显式产生(stdout 不会阻塞该数据量,因为它很容易被缓冲)。

于 2012-09-17T14:51:48.417 回答