Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 多线程上的信号量和关键部分问题_C++_Multithreading_Winapi_Semaphore_Critical Section - Fatal编程技术网

C++ 多线程上的信号量和关键部分问题

C++ 多线程上的信号量和关键部分问题,c++,multithreading,winapi,semaphore,critical-section,C++,Multithreading,Winapi,Semaphore,Critical Section,我的多线程代码有问题,希望有人能帮我解决 我希望在控制台上打印从作为参数给定的文件夹开始的所有文件和文件夹。我使用此函数进行枚举: void enumerate(char* path) { HANDLE hFind; WIN32_FIND_DATA data; char *fullpath = new char[strlen(path) - 1]; strcpy(fullpath, path); fullpath[strlen(fullpath) - 1

我的多线程代码有问题,希望有人能帮我解决

我希望在控制台上打印从作为参数给定的文件夹开始的所有文件和文件夹。我使用此函数进行枚举:

void enumerate(char* path) {
    HANDLE hFind;
    WIN32_FIND_DATA data;

    char *fullpath = new char[strlen(path) - 1];
    strcpy(fullpath, path);
    fullpath[strlen(fullpath) - 1] = '\0';

    hFind = FindFirstFile(path, &data);
    do {
        if (hFind != INVALID_HANDLE_VALUE) {
            if (strcmp(data.cFileName, ".") != 0 && strcmp(data.cFileName, ".."))
            {
                EnterCriticalSection(&crit);
                queue.push(data.cFileName);
                LeaveCriticalSection(&crit);
                ReleaseSemaphore(semaphore, 1, NULL);
                if (data.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
                {
                    strcat(fullpath, data.cFileName);
                    strcat(fullpath, "\\*");
                    enumerate(fullpath);
                }
            }
        }
    } while (FindNextFile(hFind, &data));
    FindClose(hFind);

    return;
}
当我找到一个文件或文件夹时,我希望将其添加到全局队列中,并让我的工作线程将其打印到控制台。我的辅助线程功能是:

DWORD WINAPI print_queue(LPVOID param) {
    while (1) {
        WaitForSingleObject(semaphore, INFINITE);
        EnterCriticalSection(&crit);
        char *rez = queue.front();
        queue.pop();
        LeaveCriticalSection(&crit);

        if (strcmp(rez, "DONE") == 0)
            break;
        else
            std::cout << rez << std::endl;
    }

    return 1;
}
然后创建4个线程:

thread1 = CreateThread(NULL, 0, print_queue, NULL, 0, &tId1);
thread2 = CreateThread(NULL, 0, print_queue, NULL, 0, &tId2);
thread3 = CreateThread(NULL, 0, print_queue, NULL, 0, &tId3);
thread4 = CreateThread(NULL, 0, print_queue, NULL, 0, &tId4);
然后,我调用
enumerate()
函数,并将字符串发送到队列,当到达这些字符串时,该队列将通知我的线程停止:

for (int p = 0; p<4; p++)
{
    EnterCriticalSection(&crit);
    queue.push(done);
    LeaveCriticalSection(&crit);
    ReleaseSemaphore(semaphore, 1, NULL);
}
并关闭信号灯和关键部分:

CloseHandle(semaphore);
DeleteCriticalSection(&crit);
出于某种原因,输出是随机垃圾,我不知道为什么

这是一个示例输出:

te(L┤(L ┤(L ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠°┐*╧wM3╧weµFC4 ╠╠╠╠╠ te(L)┤(L) ┤(L) ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠°┐*╧wM3╧我们使用FC4 ╠╠╠╠╠ 我的逻辑是在0上启动信号量,在队列上发生操作时进入关键部分以保护我的数据,在
enumerate()
函数中增加信号量,在
print\u queue()中减少信号量

可能有什么问题?

enumerate()
有很多问题:

  • 您没有正确使用
    strcpy()
    strcat()
    ,因此您正在破坏内存。您没有分配足够的内存来保存
    strcpy()
    的结果,strcpy()将复制字符直到到达空终止符。您分配的内存比需要的字符少了2个(路径中的最后一个字符和空终止符)。您应该分配
    strlen+1
    字符,而不是
    strlen-1
    字符。更糟糕的是,您正在使用
    strcat()
    将文件名连接到分配的字符串上,而无需首先重新分配字符串来为文件名腾出空间

  • 您正在泄漏分配的字符串,因为您从未为此调用
    delete[]

  • 检查strcmp(“…”
时,循环内的
if
缺失
!=0

  • 您正在将指针推入
    队列
    ,指向
    enumerate()
    本地的数据,这些数据在每次循环迭代时被覆盖,并且在
    enumerate()时超出范围你的线程希望得到稳定的数据,并且不会消失在后面。这是你的垃圾输出的根源。想想你的幸运,你的代码仅仅是输出垃圾而不是彻底崩溃。

  • 您没有正确测试
    数据.dwFileAttributes
    字段。您需要使用
    &
    (按位AND)运算符,而不是
    =
    (等于)文件夹和文件可以有多个属性,但您只对检查一个属性感兴趣,因此您必须单独测试该特定位,而忽略其余部分

  • 您确实应该使用
    std::string
    来管理字符串,并让它为您处理内存分配

    也可以考虑使用<代码> STD::文件系统< /代码>或<代码> Boo::文件系统< /代码>来处理枚举。

    此外,枚举后无需将
    “DONE”
    字符串推入队列。当线程收到信号并去提取字符串时,发现队列为空,只需退出线程

    请尝试类似以下内容:

    #include <windows.h>
    
    #include <iostream>
    #include <string>
    #include <queue>
    #include <thread>
    #include <mutex>
    #include <conditional_variable>
    
    std::queue<std::string> paths;
    std::mutex mtx;
    std::conditional_variable cv;
    bool done = false;
    
    void enumerate(const std::string &path)
    {
        std::string searchPath = path;
        if ((!searchPath.empty()) && (searchPath[searchPath.length()-1] != '\\'))
            searchPath += '\\';
    
        WIN32_FIND_DATA data;
        HANDLE hFind = FindFirstFileA((searchPath + "*").c_str(), &data);
        if (hFind != INVALID_HANDLE_VALUE)
        {
            do
            {
                if ((strcmp(data.cFileName, ".") != 0) && (strcmp(data.cFileName, "..") != 0))
                {
                    string fullpath = searchPath + data.cFileName;
    
                    {
                    std::lock_guard<std::mutex> lock(mtx);
                    paths.push(fullpath);
                    cv.notify_one();
                    }
    
                    if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                        enumerate(fullpath);
                }
            }
            while (FindNextFileA(hFind, &data));
            FindClose(hFind);
        }
    }
    
    void print_queue()
    {
        std::unique_lock<std::mutex> lock(mtx);
        while (true)
        {
            cv.wait(lock, [](){ return (!paths.empty()) || done; });
            if (paths.empty())
                return;
    
            std::string rez = paths.front();
            paths.pop();
    
            std::cout << rez << std::endl;
        }
    }
    
    int main()
    {
        std::thread thread1(print_queue);
        std::thread thread2(print_queue);
        std::thread thread3(print_queue);
        std::thread thread4(print_queue);
    
        enumerate("C:\\");
    
        done = true;
        cv.notify_all();
    
        thread1.join();
        thread2.join();
        thread3.join();
        thread4.join();
    
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    std::队列路径;
    std::互斥mtx;
    条件变量cv;
    bool done=false;
    void枚举(const std::string和path)
    {
    std::string searchPath=path;
    if((!searchPath.empty())&&(searchPath[searchPath.length()-1]!=“\\”)
    搜索路径+=“\\”;
    WIN32_查找_数据;
    HANDLE hFind=FindFirstFileA((searchPath+“*”).c_str(),&data);
    if(hFind!=无效的句柄值)
    {
    做
    {
    如果((strcmp(data.cFileName,“.”!=0)和&(strcmp(data.cFileName,“…”)!=0))
    {
    字符串fullpath=searchPath+data.cFileName;
    {
    标准:锁和防护锁(mtx);
    路径。推送(完整路径);
    cv.通知_one();
    }
    if(data.dwFileAttributes和文件属性目录)
    枚举(完整路径);
    }
    }
    while(FindNextFileA(hFind和data));
    FindClose(hFind);
    }
    }
    无效打印队列()
    {
    std::唯一锁(mtx);
    while(true)
    {
    cv.wait(lock,[](){return(!path.empty())| | done;});
    if(path.empty())
    返回;
    std::string rez=path.front();
    path.pop();
    std::cout
    enumerate()
    有许多问题:

    • 您没有正确使用
      strcpy()
      strcat()
      ,因此您正在破坏内存。您没有分配足够的内存来保存
      strcpy()
      的结果,strcpy()将复制字符直到到达空终止符。您分配的内存比需要的字符少了2个(路径中的最后一个字符和空终止符)。您应该分配
      strlen+1
      字符,而不是
      strlen-1
      字符。更糟糕的是,您正在使用
      strcat()
      将文件名连接到分配的字符串上,而无需首先重新分配字符串来为文件名腾出空间

    • 您正在泄漏分配的字符串,因为您从未为此调用
      delete[]

    • 循环内的
      if
      缺失
      !=0
      te(L┤(L ┤(L ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠°┐*╧wM3╧weµFC4 ╠╠╠╠╠
      #include <windows.h>
      
      #include <iostream>
      #include <string>
      #include <queue>
      #include <thread>
      #include <mutex>
      #include <conditional_variable>
      
      std::queue<std::string> paths;
      std::mutex mtx;
      std::conditional_variable cv;
      bool done = false;
      
      void enumerate(const std::string &path)
      {
          std::string searchPath = path;
          if ((!searchPath.empty()) && (searchPath[searchPath.length()-1] != '\\'))
              searchPath += '\\';
      
          WIN32_FIND_DATA data;
          HANDLE hFind = FindFirstFileA((searchPath + "*").c_str(), &data);
          if (hFind != INVALID_HANDLE_VALUE)
          {
              do
              {
                  if ((strcmp(data.cFileName, ".") != 0) && (strcmp(data.cFileName, "..") != 0))
                  {
                      string fullpath = searchPath + data.cFileName;
      
                      {
                      std::lock_guard<std::mutex> lock(mtx);
                      paths.push(fullpath);
                      cv.notify_one();
                      }
      
                      if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                          enumerate(fullpath);
                  }
              }
              while (FindNextFileA(hFind, &data));
              FindClose(hFind);
          }
      }
      
      void print_queue()
      {
          std::unique_lock<std::mutex> lock(mtx);
          while (true)
          {
              cv.wait(lock, [](){ return (!paths.empty()) || done; });
              if (paths.empty())
                  return;
      
              std::string rez = paths.front();
              paths.pop();
      
              std::cout << rez << std::endl;
          }
      }
      
      int main()
      {
          std::thread thread1(print_queue);
          std::thread thread2(print_queue);
          std::thread thread3(print_queue);
          std::thread thread4(print_queue);
      
          enumerate("C:\\");
      
          done = true;
          cv.notify_all();
      
          thread1.join();
          thread2.join();
          thread3.join();
          thread4.join();
      
          return 0;
      }