ReleaseSemaphore不会释放信号量

时间:2010-03-03 21:31:11

标签: c++ windows multithreading synchronization semaphore

(简而言之: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);
}

5 个答案:

答案 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);
}