2011-09-09 6 views
6

Mój program jest skonfigurowany w następujący sposób:
Istnieje klasa kolejki bezpiecznej dla wątków, jeden wątek przesyła na nią dane, gdy siedzi w nieskończonej pętli, a drugi wątek wychodzi z danych to siedząc w nieskończonej pętli. Próbuję wymyślić sposób użycia zdarzeń systemu Windows lub innego mechanizmu, aby wątek_1 (poniżej), czekać w nieskończonej pętli while i tylko iterować, gdy głębokość kolejki jest większa lub równa 1.windows C++ wątek oczekujący na kolejkę danych-push

class thread-safe_Queue 
{ 
public: 
    push(); 
    pop(); 
}; 

DWORD thread_1() 
{ 
while(1) 
{ 
    // wait for thread-safe queue to have data on it 
    // pop data off 
    // process data 
} 
} 

DWORD thread_2() 
{ 
while(1) 
{ 
    // when data becomes available, push data onto thread-safe queue 
} 
} 

Odpowiedz

0

Co powiesz na ten (zakładam, że znasz mechanizm zdarzeń).

1.

thread_safe_Queue::push(something) 
{ 
// lock the queue 
... 
// push object 
// Signal the event 
SetEvent(notification); 

// unlock the queue 
} 

2.

thread_safe_Queue::pop(something) 
{ 
WaitForSingleObject(notification); 
// lock the queue 
... 
// get object 
// reset the event 
if (queue is empty) 
    ResetEvent(notification); 

// unlock the queue 
} 

3. thread_1 właśnie próbuje pop obiekt i poradzić. Gdy coś jest pchane, zdarzenie jest włączone, dzięki czemu można z powodzeniem wywołać pop. W przeciwnym razie będzie czekać wewnątrz pop. Właściwie możesz użyć innych obiektów synchronizujących, takich jak muteksy lub sekcje krytyczne, zamiast zdarzeń w tym przypadku.

AKTUALIZACJA. zdarzenia zewnętrzne: gwintu 1:

void thread_1() 
    { 
    while(1) 
    { 
    WaitForSingleObject(notification); 
    if (!pop(object)) // pop should return if there are any objects left in queue 
     SetEvent(notification);  
    } 
    } 

thread_2

void thread_2() 
    { 
    while(1) 
    { 
    // push the object and than signal event 
    ResetEvent(notification) 
    } 
    } 
+0

Wolałbym, aby zdarzenia znajdowały się poza klasą i wewnątrz funkcji punktu wejścia wątku. Powodem jest drugie wydarzenie, na które wątki również czekają. To jest, gdy użytkownik chce zakończyć program, a tym samym zakończyć nieskończoną pętlę while. Kiedy tak się stanie, użytkownik wyśle ​​polecenie zamknięcia programu, wątek push przestanie nasłuchiwać danych i zamknie się, a wątek pchania przestanie czekać na to, że wątek będzie zawierał dane, a także zamknie się. – rossb83

+0

Możesz zrobić to samo ze zdarzeniami zewnętrznymi. Zaktualizowałem powyższą odpowiedź. – Werolik

+0

czy nie miałeś na myśli, aby wątek1 wywoływał reset, a wątek 2 wywoływał zestaw w zewnętrznej wersji? Ponadto, Jak uniknąć impasu w tym scenariuszu: 1. wątek1 nie uda się pop. 2. zestaw wywołań thread2. 3. Resetowanie wątku1. – Nir

0

Można używać nazwanych zdarzeń. Każdy wątek wywoływałby przekazywanie do CreateEvent o tej samej nazwie. Następnie użyj WaitForMultipleObjects, aby poczekać na zdarzenie związane z kolejką lub zdarzenie końcowe. Wątek popu czekałby na zdarzenia queue_has_data i end_program. Wątek wypychania będzie czekał na zdarzenia data_available i end_program i ustawi zdarzenie queue_has_data, gdy wstawi coś do kolejki.

2

Myślę, że to może załatwić sprawę. Wylicz klasę Event i przeciąż funkcję Process().

#include <process.h> // Along with all the normal windows includes 

//********************************************* 
using namespace os; 

Mutex globalQueueMutex; 

class QueueReader : public Event 
{ 
public: 
    virtual void Process() 
    { 
     // Lock the queue 
     Locker l(globalQueueMutex); 
     // pop data off 
     // process data 
     return; // queue will automatically unlock 
    } 
}; 

QueueReader myQueueReader; 

//********************************************* 
// The queue writer would have functions like : 
void StartQueueReader() 
{ 
    Thread(QueueReader::StartEventHandler, &myQueueReader); 
} 
void WriteToQueue() 
{ 
    Locker l(globalQueueMutex); 
    // write to the queue 
    myQueueReader.SignalProcess(); // tell reader to wake up 
} 
// When want to shutdown 
void Shutdown() 
{ 
    myQueueReader.SignalShutdown(); 
} 

Oto klasy, które wykonują magię.

namespace os { 

// ********************************************************************** 
/// Windows implementation to spawn a thread. 
static uintptr_t Thread (void (*StartAddress)(void *), void *ArgList) 
{ 
    return _beginthread(StartAddress, 0, ArgList); 
} 

// ********************************************************************** 
/// Windows implementation of a critical section. 
class Mutex 
{ 
public: 
    // Initialize section on construction 
    Mutex() { InitializeCriticalSection(&cs_); } 
    // Delete section on destruction 
    ~Mutex() { DeleteCriticalSection(&cs_); } 
    // Lock it 
    void lock() { EnterCriticalSection(&cs_); } 
    // Unlock it 
    void unlock() { LeaveCriticalSection(&cs_); } 

private: 
    CRITICAL_SECTION cs_; 
}; // class Mutex 

/// Locks/Unlocks a mutex 
class Locker 
{ 
public: 
    // Lock the mutex on construction 
    Locker(Mutex& mutex): mutex_(mutex) { mutex_.lock(); } 
    // Unlock on destruction 
    ~Locker() { mutex_.unlock(); } 
private: 
    Mutex& mutex_; 
}; // class Locker 

// ********************************************************************** 
// Windows implementation of event handler 
#define ProcessEvent hEvents[0] 
#define SetTimerEvent hEvents[1] 
#define ShutdownEvent hEvents[2] 

/// Windows implementation of events 
class Event 
{ 
    /// Flag set when shutdown is complete 
    bool Shutdown; 
    /// Max time to wait for events 
    DWORD Timer; 
    /// The three events - process, reset timer, and shutdown 
    HANDLE hEvents[3]; 

public: 
    /// Timeout is disabled by default and Events assigned 
    Event(DWORD timer = INFINITE) : Timer(timer) 
    { 
    Shutdown = false; 
    ProcessEvent = CreateEvent(NULL,TRUE,FALSE,NULL); 
    SetTimerEvent = CreateEvent(NULL,TRUE,FALSE,NULL); 
    ShutdownEvent = CreateEvent(NULL,TRUE,FALSE,NULL); 
    } 

    /// Close the event handles 
    virtual ~Event() 
    { 
    CloseHandle(ProcessEvent); 
    CloseHandle(SetTimerEvent); 
    CloseHandle(ShutdownEvent); 
    } 

    /// os::Thread calls this to start the Event handler 
    static void StartEventHandler(void *pMyInstance) 
    { ((Event *)pMyInstance)->EventHandler(); } 
    /// Call here to Change/Reset the timeout timer 
    void ResetTimer(DWORD timer) { Timer = timer; SetEvent(SetTimerEvent); } 
    /// Set the signal to shutdown the worker thread processing events 
    void SignalShutdown() { SetEvent(ShutdownEvent); while (!Shutdown) Sleep(30);} 
    /// Set the signal to run the process 
    void SignalProcess() { SetEvent(ProcessEvent); } 

protected: 
    /// Overload in derived class to process events with worker thread 
    virtual void Process(){} 
    /// Override to process timeout- return true to terminate thread 
    virtual bool Timeout(){ return true;} 

    /// Monitor thread events 
    void EventHandler() 
    { 
    DWORD WaitEvents; 
    while (!Shutdown) 
    { 
     // Wait here, looking to be signaled what to do next 
     WaitEvents = WaitForMultipleObjects(3, hEvents, FALSE, Timer); 

     switch (WaitEvents) 
     { 
     // Process event - process event then reset for the next one 
     case WAIT_OBJECT_0 + 0: 
      Process(); 
      ResetEvent(ProcessEvent); 
      break; 

     // Change timer event - see ResetTimer(DWORD timer) 
     case WAIT_OBJECT_0 + 1: 
      ResetEvent(SetTimerEvent); 
      continue; 

     // Shutdown requested so exit this thread 
     case WAIT_OBJECT_0 + 2: 
      Shutdown = true; 
      break; 

     // Timed out waiting for an event 
     case WAIT_TIMEOUT: 
      Shutdown = Timeout(); 
      break; 

     // Failed - should never happen 
     case WAIT_FAILED: 
      break; 

     default: 
      break; 
     } 
    } 
    } 


}; 

} // namespace os