【问题标题】:CreateThread ends with bad outputCreateThread 以错误的输出结束
【发布时间】:2019-07-01 16:07:22
【问题描述】:

我正在研究多线程。我将 Win32Api 用于 CreateThread。 我有 char 数组,其中包含 5 个驱动器号。我需要将这些驱动器一个一个地MessageBox。

这是我的代码:

DWORD WINAPI Fun(LPVOID param)
{
const char* str = (const char*)param;
MessageBox(NULL, str, "hello", MB_OK | MB_ICONQUESTION);
return 0;
}

void StartWork()
{
int n, d, b = 0;
char dd;
DWORD dr = GetLogicalDrives();

HANDLE threads[26];

for (int i = 0; i < 26; i++)
{
    n = ((dr >> i) & 1);
    if (n == 1)
    {
        dd = char(65 + i);
        std::string text(1, dd);
        d = GetDriveType((text + ":\\").c_str());
        if (d == DRIVE_REMOVABLE || d == DRIVE_FIXED || d == DRIVE_REMOTE)
        {
            threads[b] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Evil, (LPVOID)text.c_str(), 0, NULL);
            b += 1;
        }
    }
}
WaitForMultipleObjects(b, threads, TRUE, 1000);
}

输出不是我想要的。我刚刚收到最后一个磁盘号(我有 3 个磁盘 - C、D、E,我的输出是 msgbox "E" 的 3 倍)

【问题讨论】:

  • 您正在向 CreateThread 传递一个指向字符串文本内部缓冲区的指针。文本超出范围时会被销毁。其次std::string是c++,为什么这个问题标记为C?
  • 进一步,如果你“需要一个个的MessageBox这些驱动器”,为什么首先需要并发?
  • @veter0 正如 CuriouslyRecurringThoughts 所指出的,您需要正确使用缓冲区。您可以动态分配,将其发送到线程函数,然后从那里释放它。提出您的问题,您真正拥有什么类型的驱动器?确保它在您提供的 3 种类型中,而不是 DRIVE_CDROM 或其他类型。
  • 使用std::thread 摆脱所有这些强制转换,让自己从一些评论者建议的手动内存管理中解脱出来。 CreateThread() 并不真正适合现代 C++ 程序。使用std::thread,您只需按值传递std::string 即可。
  • int n, d, bchar ddint i。你不觉得char 的变量名有点太,呃,冗长吗?无论如何,我们不知道您的问题是什么,因为我们看不到Evil

标签: c++ multithreading winapi createthread


【解决方案1】:

我假设在您的示例中EvilFun。现在,我要编写的代码不是好的现代代码(虽然我使用的是 C++11,请参阅constexpr),但我希望它足以向您展示问题。 strings 必须存活到程序结束(或至少直到所有线程都完成):

void StartWork()
{
  int n, d, b = 0;
  char dd;
  DWORD dr = GetLogicalDrives();

  constexpr std::size_t numMaxThreads = 26;
  HANDLE threads[numMaxThreads];

  //Now the strings survive until the end of the program, #include <array>
  std::array<std::string, numMaxThreads> texts;
  for (int i = 0; i < numMaxThreads; i++)
  {
    n = ((dr >> i) & 1);
    if (n == 1)
    {
        dd = char(65 + i);
        std::string text(1, dd);
        texts[b] = std::move(text);
        d = GetDriveType((texts[b] + ":\\").c_str());
        if (d == DRIVE_REMOVABLE || d == DRIVE_FIXED || d == DRIVE_REMOTE)
        {
            threads[b] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Fun, (LPVOID)texts[b].c_str(), 0, NULL);
            b += 1;
        }
    }
  }
  WaitForMultipleObjects(b, threads, TRUE, 1000);
}

现在,这是好的现代代码吗?不,我建议使用std::thread:这将允许您更好地处理strings 的生命周期,类似这样的

#include <string>
#include <vector>
#include <thread>
#include <Windows.h>

void Fun(const std::string& str)
{
    MessageBox(NULL, str.c_str(), "hello", MB_OK | MB_ICONQUESTION);
}

void StartWork()
{
    int n, d;
    char dd;
    DWORD dr = GetLogicalDrives();

    std::vector<std::thread> threads;

    for (int i = 0; i < 26; i++)
    {
        n = ((dr >> i) & 1);
        if (n == 1)
        {
            dd = char(65 + i);
            std::string text(1, dd);
            d = GetDriveType((text + ":\\").c_str());
            if (d == DRIVE_REMOVABLE || d == DRIVE_FIXED || d == DRIVE_REMOTE)
            {
                //thanks to zett42 for this simplification
                threads.emplace_back(Fun, text);
            }
        }
    }
    //We could be using for_each also
    for (auto& thread : threads) { thread.join(); }
}

请注意:

  1. 使用std::thread,您可以避免令人头疼的内存管理,将内存的所有权传递给线程,然后由线程负责清理
  2. 您可以放弃使用 void 指针和手动转换内容的需要,从而提高类型安全性。

编辑:用户 zett42 建议了一个更简单的实现,我更新了答案。

【讨论】:

  • 更简单:threads.emplace_back(Fun, text);。此外,WINAPI 可以从线程函数签名中删除。
  • @zett42 你说得对,谢谢你指出,答案更新了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2014-02-08
相关资源
最近更新 更多