(简而言之:main()的WaitForSingleObject在下面的程序中挂起。)
我正在尝试编写一段代码来调度线程并在它恢复之前等待它们完成。我不是每次创建线程都是昂贵的,而是让它们进入睡眠状态。主线程在CREATE_SUSPENDED状态下创建X线程。
使用X作为MaximumCount的信号量完成同步。信号量的计数器被降低到零并且线程被分派。 threds执行一些愚蠢的循环并在他们进入睡眠之前调用ReleaseSemaphore。然后主线程使用WaitForSingleObject X次,以确保每个线程完成其工作并正在休眠。然后它循环并再次完成。
程序有时不会退出。当我喙程序时,我可以看到WaitForSingleObject挂起。这意味着线程的ReleaseSemaphore不起作用。没有什么是打印的,所以据说没有出错。
也许两个线程不应该在同一时间调用ReleaseSemaphore,但这会使信号量的目的无效......
我只是不喜欢它......
感谢接受同步线程的其他解决方案!
#define TRY 100
#define LOOP 100
HANDLE *ids;
HANDLE semaphore;
DWORD WINAPI Count(__in LPVOID lpParameter)
{
float x = 1.0f;
while(1)
{
for (int i=1 ; i<LOOP ; i++)
x = sqrt((float)i*x);
while (ReleaseSemaphore(semaphore,1,NULL) == FALSE)
printf(" ReleaseSemaphore error : %d ", GetLastError());
SuspendThread(ids[(int) lpParameter]);
}
return (DWORD)(int)x;
}
int main()
{
SYSTEM_INFO sysinfo;
GetSystemInfo( &sysinfo );
int numCPU = sysinfo.dwNumberOfProcessors;
semaphore = CreateSemaphore(NULL, numCPU, numCPU, NULL);
ids = new HANDLE[numCPU];
for (int j=0 ; j<numCPU ; j++)
ids[j] = CreateThread(NULL, 0, Count, (LPVOID)j, CREATE_SUSPENDED, NULL);
for (int j=0 ; j<TRY ; j++)
{
for (int i=0 ; i<numCPU ; i++)
{
if (WaitForSingleObject(semaphore,1) == WAIT_TIMEOUT)
printf("Timed out !!!\n");
ResumeThread(ids[i]);
}
for (int i=0 ; i<numCPU ; i++)
WaitForSingleObject(semaphore,INFINITE);
ReleaseSemaphore(semaphore,numCPU,NULL);
}
CloseHandle(semaphore);
printf("Done\n");
getc(stdin);
}
答案 0 :(得分:4)
我总是使用线程安全队列,而不是使用信号量(至少直接)或主要显式唤醒线程来完成一些工作。当main希望工作线程执行某些操作时,它会将要完成的作业的描述推送到队列中。每个工作线程只做一个工作,然后尝试从队列中弹出另一个工作,并最终暂停,直到队列中有一个工作为他们做:
队列的代码如下所示:
#ifndef QUEUE_H_INCLUDED
#define QUEUE_H_INCLUDED
#include <windows.h>
template<class T, unsigned max = 256>
class queue {
HANDLE space_avail; // at least one slot empty
HANDLE data_avail; // at least one slot full
CRITICAL_SECTION mutex; // protect buffer, in_pos, out_pos
T buffer[max];
long in_pos, out_pos;
public:
queue() : in_pos(0), out_pos(0) {
space_avail = CreateSemaphore(NULL, max, max, NULL);
data_avail = CreateSemaphore(NULL, 0, max, NULL);
InitializeCriticalSection(&mutex);
}
void push(T data) {
WaitForSingleObject(space_avail, INFINITE);
EnterCriticalSection(&mutex);
buffer[in_pos] = data;
in_pos = (in_pos + 1) % max;
LeaveCriticalSection(&mutex);
ReleaseSemaphore(data_avail, 1, NULL);
}
T pop() {
WaitForSingleObject(data_avail,INFINITE);
EnterCriticalSection(&mutex);
T retval = buffer[out_pos];
out_pos = (out_pos + 1) % max;
LeaveCriticalSection(&mutex);
ReleaseSemaphore(space_avail, 1, NULL);
return retval;
}
~queue() {
DeleteCriticalSection(&mutex);
CloseHandle(data_avail);
CloseHandle(space_avail);
}
};
#endif
在线程中使用它的代码的粗略等价看起来像这样。我没有弄清楚你的线程函数到底在做什么,但它总结了平方根,显然你对线程同步比对线程实际做的更感兴趣。
编辑:(根据评论):
如果你需要main()
等待一些任务完成,做更多工作,然后分配更多任务,通常最好通过将事件(例如)放入每个任务来处理它,并让你的线程功能设置事件。修改后的代码看起来像这样(注意队列代码不受影响):
#include "queue.hpp"
#include <iostream>
#include <process.h>
#include <math.h>
#include <vector>
struct task {
int val;
HANDLE e;
task() : e(CreateEvent(NULL, 0, 0, NULL)) { }
task(int i) : val(i), e(CreateEvent(NULL, 0, 0, NULL)) {}
};
void process(void *p) {
queue<task> &q = *static_cast<queue<task> *>(p);
task t;
while ( -1 != (t=q.pop()).val) {
std::cout << t.val << "\n";
SetEvent(t.e);
}
}
int main() {
queue<task> jobs;
enum { thread_count = 4 };
enum { task_count = 10 };
std::vector<HANDLE> threads;
std::vector<HANDLE> events;
std::cout << "Creating thread pool" << std::endl;
for (int t=0; t<thread_count; ++t)
threads.push_back((HANDLE)_beginthread(process, 0, &jobs));
std::cout << "Thread pool Waiting" << std::endl;
std::cout << "First round of tasks" << std::endl;
for (int i=0; i<task_count; ++i) {
task t(i+1);
events.push_back(t.e);
jobs.push(t);
}
WaitForMultipleObjects(events.size(), &events[0], TRUE, INFINITE);
events.clear();
std::cout << "Second round of tasks" << std::endl;
for (int i=0; i<task_count; ++i) {
task t(i+20);
events.push_back(t.e);
jobs.push(t);
}
WaitForMultipleObjects(events.size(), &events[0], true, INFINITE);
events.clear();
for (int j=0; j<thread_count; ++j)
jobs.push(-1);
WaitForMultipleObjects(threads.size(), &threads[0], TRUE, INFINITE);
return 0;
}
答案 1 :(得分:3)
我不了解代码,但线程同步绝对不好。您假设线程将按特定顺序调用SuspendThread()。成功的WaitForSingleObject()调用不会告诉您哪个线程名为ReleaseSemaphore()。因此,您将在未挂起的线程上调用ReleaseThread()。这很快使程序陷入僵局。
另一个错误的假设是在WFSO返回后已经调用了SuspendThread的线程。通常是的,并非总是如此。在RS呼叫之后,线程可以被抢占。您将再次在未挂起的线程上调用ReleaseThread()。通常需要一天左右的时间来使你的程序陷入僵局。
我认为有一个ReleaseSemaphore调用太多了。毫无疑问,试图解开它。
您无法使用Suspend / ReleaseThread()控制线程,请勿尝试。
答案 2 :(得分:3)
在以下情况下会出现问题:
主线程恢复工作线程:
for (int i=0 ; i<numCPU ; i++)
{
if (WaitForSingleObject(semaphore,1) == WAIT_TIMEOUT)
printf("Timed out !!!\n");
ResumeThread(ids[i]);
}
工作线程完成工作并释放信号量:
for (int i=1 ; i<LOOP ; i++)
x = sqrt((float)i*x);
while (ReleaseSemaphore(semaphore,1,NULL) == FALSE)
主线程等待所有工作线程并重置信号量:
for (int i=0 ; i<numCPU ; i++)
WaitForSingleObject(semaphore,INFINITE);
ReleaseSemaphore(semaphore,numCPU,NULL);
主线程进入下一轮,试图恢复工作线程(请注意,工作线程还没有事件暂停!这就是问题开始的地方......你正在尝试恢复那些没有的线程' t必然暂停):
for (int i=0 ; i<numCPU ; i++)
{
if (WaitForSingleObject(semaphore,1) == WAIT_TIMEOUT)
printf("Timed out !!!\n");
ResumeThread(ids[i]);
}
最后工作线程暂停(尽管它们应该已经开始下一轮):
SuspendThread(ids[(int) lpParameter]);
并且主线程永远等待,因为现在所有工人都被暂停:
for (int i=0 ; i<numCPU ; i++)
WaitForSingleObject(semaphore,INFINITE);
这是一个链接,显示如何正确解决生产者/消费者问题:
http://en.wikipedia.org/wiki/Producer-consumer_problem
我认为critical sections比信号量和互斥量快得多。在大多数情况下(imo),它们也更容易理解。
答案 3 :(得分:0)
问题在于你比等信号更频繁地等待。
for (int j=0 ; j<TRY ; j++)
循环等待信号量八次,而四个线程只发出一次信号,循环本身发出一次信号。第一次通过循环,这不是问题,因为信号量的初始计数为4。第二次以及随后的每一次,你都在等待太多的信号。这可以通过以下事实得到缓解:在前四次等待时,您可以限制时间并且不会在出错时重试。所以有时它可能会起作用,有时你的等待会挂起。
我认为以下(未经测试的)更改会有所帮助。
将信号量初始化为零计数:
semaphore = CreateSemaphore(NULL, 0, numCPU, NULL);
摆脱线程恢复循环中的等待(即删除以下内容):
if (WaitForSingleObject(semaphore,1) == WAIT_TIMEOUT)
printf("Timed out !!!\n");
从try循环结束时删除无关信号(即删除以下内容):
ReleaseSemaphore(semaphore,numCPU,NULL);
答案 4 :(得分:0)
这是一个实用的解决方案。
我希望我的主程序使用线程(然后使用多个核心)来修复作业并等待所有线程完成,然后再恢复并执行其他操作。我不想让线程死掉并创建新线程,因为这很慢。在我的问题中,我试图通过暂停线程来做到这一点,这似乎很自然。但正如nobugz指出的那样,“你可以用Suspend / ReleaseThread()控制线程化。”
解决方案涉及信号量,就像我用来控制线程的信号量一样。实际上还有一个信号量用来控制主线程。现在我每个线程有一个信号量来控制线程,一个信号量控制主线。
以下是解决方案:
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <process.h>
#define TRY 500000
#define LOOP 100
HANDLE *ids;
HANDLE *semaphores;
HANDLE allThreadsSemaphore;
DWORD WINAPI Count(__in LPVOID lpParameter)
{
float x = 1.0f;
while(1)
{
WaitForSingleObject(semaphores[(int)lpParameter],INFINITE);
for (int i=1 ; i<LOOP ; i++)
x = sqrt((float)i*x+rand());
ReleaseSemaphore(allThreadsSemaphore,1,NULL);
}
return (DWORD)(int)x;
}
int main()
{
SYSTEM_INFO sysinfo;
GetSystemInfo( &sysinfo );
int numCPU = sysinfo.dwNumberOfProcessors;
ids = new HANDLE[numCPU];
semaphores = new HANDLE[numCPU];
for (int j=0 ; j<numCPU ; j++)
{
ids[j] = CreateThread(NULL, 0, Count, (LPVOID)j, NULL, NULL);
// Threads blocked until main releases them one by one
semaphores[j] = CreateSemaphore(NULL, 0, 1, NULL);
}
// Blocks main until threads finish
allThreadsSemaphore = CreateSemaphore(NULL, 0, numCPU, NULL);
for (int j=0 ; j<TRY ; j++)
{
for (int i=0 ; i<numCPU ; i++) // Let numCPU threads do their jobs
ReleaseSemaphore(semaphores[i],1,NULL);
for (int i=0 ; i<numCPU ; i++) // wait for numCPU threads to finish
WaitForSingleObject(allThreadsSemaphore,INFINITE);
}
for (int j=0 ; j<numCPU ; j++)
CloseHandle(semaphores[j]);
CloseHandle(allThreadsSemaphore);
printf("Done\n");
getc(stdin);
}