【问题标题】:CreateThread x3000 returns stdout handleCreateThread x3000 返回标准输出句柄
【发布时间】:2017-12-17 14:53:13
【问题描述】:

我在创建数千个线程并关闭它时遇到问题。 看这段代码:

HANDLE threadHandles[i];
for(int i = 0; i < 1000; i++)
{
    CreateThread(0, 0, &func, 0, CREATE_SUSPENDED, threadHandles[i]);
    printf("%i - %i\n", i, threadHandles[i]);
    CloseHandle(threadHandles[i])
}
printf("Last Error is %i", GetLastError());

它应该继续这个输出:

0 - 236423
1 - 23456236
2 - 2373547 
3 - 73521346 
4 - 23456775
5 - 78543683465 
...
2998 - 754752
2999 - 23462346
Last Error is 0

像这样。

但实际上它不会打印任何内容。为什么?因为其中一个创建的线程与标准输出句柄发生冲突。 我是用这段代码实现的:

HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE threadHandles[i];
for(int i = 0; i < 1000; i++)
{
    CreateThread(0, 0, &func, 0, CREATE_SUSPENDED, threadHandles[i]);
    printf("%i - %i\n", i, threadHandles[i]);
    if(threadHandles[i] != stdout) // I have stdout == 12 on my machine
        CloseHandle(threadHandles[i])
}

它是这样工作的:

0 - 236423
1 - 23456236
2 - 2373547 
3 - 0 
4 - 23456775
5 - 78543683465 
...
2998 - 0
2999 - 23462346
Last Error is 6

问题出在哪里?为什么创建的句柄和现有的标准句柄有冲突?

【问题讨论】:

  • 在问题标题和提供的输出中,您建议循环计数应为 3,000 - 但在代码中,它只有 1,000。这是您使用的确切代码吗?
  • 这只是一个例子
  • 你打印 threadId 而不是线程句柄。但即使你的 printf 不正确 - 0 - 236423236423 不能是始终具有 4*n 形式的线程 ID

标签: c winapi


【解决方案1】:

线程句柄由函数返回!最后一个参数接收线程id,而不是句柄。

HANDLE threadHandles[1000] = { 0 };
UINT i;
for (i = 0; i < 1000; ++i)
{
    DWORD threadId;
    threadHandles[i] = CreateThread(0, 0, &func, 0, /*CREATE_SUSPENDED*/0, &threadId);
    if (!threadHandles[i])
    {
        printf("Failed to create thread, error %u\n", GetLastError());
        break;
    }
    printf("Thread #%u: handle=%p id=%u\n", i, threadHandles[i], threadId);
    // Not closing the handle here so the example will show unique thread handles and ids: CloseHandle(threadHandles[i]), threadHandles[i] = NULL;
}

// TODO: Wait for threads or do some other work?

for (i = 0; i < 1000; ++i)
    if (threadHandles[i])
        CloseHandle(threadHandles[i]);

在我的机器上打印

Thread #0: handle=0000002C id=16424
Thread #1: handle=00000030 id=21192
Thread #2: handle=00000034 id=21180
Thread #3: handle=00000038 id=17336
Thread #4: handle=0000003C id=21184
Thread #5: handle=00000040 id=4460
...
Thread #991: handle=00000FE8 id=12280
Thread #992: handle=00000FEC id=20360
Thread #993: handle=00000FF0 id=20328
Thread #994: handle=00000FF4 id=16060
Thread #995: handle=00000FF8 id=4556
Thread #996: handle=00000FFC id=20296
Thread #997: handle=00001004 id=10316
Thread #998: handle=00001008 id=20604
Thread #999: handle=0000100C id=20264

【讨论】:

  • 不能返回CreateThread的lpThreadId参数吗? UPD:我用您的更改编辑我的代码,但现在我有错误 8 并输出:0 - 236423 1 - 23456236 2 - 2373547 3 - 34573 4 - 23456775 5 - 78543683465 ... 2998 - 0 2999 - 0
  • 来自msdn - “默认情况下,每个线程都有 1 MB 的堆栈空间。因此,您最多可以创建 2,048 个线程。”所以,创建 3,000 个线程是行不通的。此外,线程 ID 和线程句柄是不同的东西。
  • 您还应该记住,当线程完成并且它的所有句柄都已关闭时,句柄值和线程ID都可能被重用。线程 id 仅在线程有一个有效句柄打开时才有效。
  • 在我的机器上,你和我的代码都在 900 个线程的限制下中断!有什么问题?
  • 8 是 ERROR_NOT_ENOUGH_MEMORY。如果旧线程已结束并且句柄已关闭,则 句柄值 可以相同。一旦对象本身消失,句柄值就可以重用,这就是它的工作方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-07
  • 2012-11-12
相关资源
最近更新 更多