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::coutenumerate()
有许多问题:
- 您没有正确使用
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;
}