7-1
ReadWrit.h
/*
* ReadWrit.h
*
* Sample code for Multithreading Applications in Win32
* This is from Chapter 7, Listing 7-1
*
* Demonstrates an implementation of the
* Readers/Writers algorithm. This version
* gives preference to readers.
*/
///////////////////////////////////////////////////////
//
// Structure definition
//
typedef struct _RWLock
{
// Handle to a mutex that allows
// a single reader at a time access
// to the reader counter.
HANDLE hMutex;
// Handle to a semaphore that keeps
// the data locked for either the
// readers or the writers.
HANDLE hDataLock;
// The count of the number of readers.
// Can legally be zero or one while
// a writer has the data locked.
int nReaderCount;
} RWLock;
//
// Reader/Writer prototypes
//
BOOL InitRWLock(RWLock *pLock);
BOOL DestroyRWLock(RWLock *pLock);
BOOL AcquireReadLock(RWLock *pLock);
int ReleaseReadLock(RWLock *pLock);
BOOL AcquireWriteLock(RWLock *pLock);
int ReleaseWriteLock(RWLock *pLock);
BOOL ReadOK(RWLock *pLock);
BOOL WriteOK(RWLock *pLock);
BOOL FatalError(char *s);
* ReadWrit.h
*
* Sample code for Multithreading Applications in Win32
* This is from Chapter 7, Listing 7-1
*
* Demonstrates an implementation of the
* Readers/Writers algorithm. This version
* gives preference to readers.
*/
///////////////////////////////////////////////////////
//
// Structure definition
//
typedef struct _RWLock
{
// Handle to a mutex that allows
// a single reader at a time access
// to the reader counter.
HANDLE hMutex;
// Handle to a semaphore that keeps
// the data locked for either the
// readers or the writers.
HANDLE hDataLock;
// The count of the number of readers.
// Can legally be zero or one while
// a writer has the data locked.
int nReaderCount;
} RWLock;
//
// Reader/Writer prototypes
//
BOOL InitRWLock(RWLock *pLock);
BOOL DestroyRWLock(RWLock *pLock);
BOOL AcquireReadLock(RWLock *pLock);
int ReleaseReadLock(RWLock *pLock);
BOOL AcquireWriteLock(RWLock *pLock);
int ReleaseWriteLock(RWLock *pLock);
BOOL ReadOK(RWLock *pLock);
BOOL WriteOK(RWLock *pLock);
BOOL FatalError(char *s);
代码
/*
* ReadWrit.c
*
* Sample code for "Multithreading Applications in Win32"
* This is from Chapter 7, various listings.
*
* Demonstrates an implementation of the
* Readers/Writers algorithm. This version
* gives preference to readers.
*/
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "ReadWrit.h"
// If we wait more than 2 seconds, then something is probably wrong!
#define MAXIMUM_TIMEOUT 2000
// Here's the pseudocode for what is going on:
//
// Lock for Reader:
// Lock the mutex
// Bump the count of readers
// If this is the first reader, lock the data
// Release the mutex
//
// Unlock for Reader:
// Lock the mutex
// Decrement the count of readers
// If this is the last reader, unlock the data
// Release the mutex
//
// Lock for Writer:
// Lock the data
//
// Unlock for Reader:
// Unlock the data
///////////////////////////////////////////////////////
BOOL MyWaitForSingleObject(HANDLE hObject)
{
DWORD result;
result = WaitForSingleObject(hObject, MAXIMUM_TIMEOUT);
// Comment this out if you want this to be non-fatal
if (result != WAIT_OBJECT_0)
FatalError("MyWaitForSingleObject - Wait failed, you probably forgot to call release!");
return (result == WAIT_OBJECT_0);
}
BOOL InitRWLock(RWLock *pLock)
{
pLock->nReaderCount = 0;
pLock->hDataLock = CreateSemaphore(NULL, 1, 1, NULL);
if (pLock->hDataLock == NULL)
return FALSE;
pLock->hMutex = CreateMutex(NULL, FALSE, NULL);
if (pLock->hMutex == NULL)
{
CloseHandle(pLock->hDataLock);
return FALSE;
}
return TRUE;
}
BOOL DestroyRWLock(RWLock *pLock)
{
DWORD result = WaitForSingleObject(pLock->hDataLock, 0);
if (result == WAIT_TIMEOUT)
return FatalError("DestroyRWLock - Can't destroy object, it's locked!");
CloseHandle(pLock->hMutex);
CloseHandle(pLock->hDataLock);
return TRUE;
}
BOOL AcquireReadLock(RWLock *pLock)
{
BOOL result = TRUE;
if (!MyWaitForSingleObject(pLock->hMutex))
return FALSE;
if(++pLock->nReaderCount == 1)
result = MyWaitForSingleObject(pLock->hDataLock);
ReleaseMutex(pLock->hMutex);
return result;
}
BOOL ReleaseReadLock(RWLock *pLock)
{
int result;
LONG lPrevCount;
if (!MyWaitForSingleObject(pLock->hMutex))
return FALSE;
if (--pLock->nReaderCount == 0)
result = ReleaseSemaphore(pLock->hDataLock, 1, &lPrevCount);
ReleaseMutex(pLock->hMutex);
return result;
}
BOOL AcquireWriteLock(RWLock *pLock)
{
return MyWaitForSingleObject(pLock->hDataLock);
}
BOOL ReleaseWriteLock(RWLock *pLock)
{
int result;
LONG lPrevCount;
result = ReleaseSemaphore(pLock->hDataLock, 1, &lPrevCount);
if (lPrevCount != 0)
FatalError("ReleaseWriteLock - Semaphore was not locked!");
return result;
}
BOOL ReadOK(RWLock *pLock)
{
// This check is not perfect, because we
// do not know for sure if we are one of
// the readers.
return (pLock->nReaderCount > 0);
}
BOOL WriteOK(RWLock *pLock)
{
DWORD result;
// The first reader may be waiting in the mutex,
// but any more than that is an error.
if (pLock->nReaderCount > 1)
return FALSE;
// This check is not perfect, because we
// do not know for sure if this thread was
// the one that had the semaphore locked.
result = WaitForSingleObject(pLock->hDataLock, 0);
if (result == WAIT_TIMEOUT)
return TRUE;
// a count is kept, which was incremented in Wait.
result = ReleaseSemaphore(pLock->hDataLock, 1, NULL);
if (result == FALSE)
FatalError("WriteOK - ReleaseSemaphore failed");
return FALSE;
}
///////////////////////////////////////////////////////
/*
* Error handler
*/
BOOL FatalError(char *s)
{
fprintf(stdout, "%s\n", s);
// Comment out exit() to prevent termination
exit(EXIT_FAILURE);
return FALSE;
}
* ReadWrit.c
*
* Sample code for "Multithreading Applications in Win32"
* This is from Chapter 7, various listings.
*
* Demonstrates an implementation of the
* Readers/Writers algorithm. This version
* gives preference to readers.
*/
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "ReadWrit.h"
// If we wait more than 2 seconds, then something is probably wrong!
#define MAXIMUM_TIMEOUT 2000
// Here's the pseudocode for what is going on:
//
// Lock for Reader:
// Lock the mutex
// Bump the count of readers
// If this is the first reader, lock the data
// Release the mutex
//
// Unlock for Reader:
// Lock the mutex
// Decrement the count of readers
// If this is the last reader, unlock the data
// Release the mutex
//
// Lock for Writer:
// Lock the data
//
// Unlock for Reader:
// Unlock the data
///////////////////////////////////////////////////////
BOOL MyWaitForSingleObject(HANDLE hObject)
{
DWORD result;
result = WaitForSingleObject(hObject, MAXIMUM_TIMEOUT);
// Comment this out if you want this to be non-fatal
if (result != WAIT_OBJECT_0)
FatalError("MyWaitForSingleObject - Wait failed, you probably forgot to call release!");
return (result == WAIT_OBJECT_0);
}
BOOL InitRWLock(RWLock *pLock)
{
pLock->nReaderCount = 0;
pLock->hDataLock = CreateSemaphore(NULL, 1, 1, NULL);
if (pLock->hDataLock == NULL)
return FALSE;
pLock->hMutex = CreateMutex(NULL, FALSE, NULL);
if (pLock->hMutex == NULL)
{
CloseHandle(pLock->hDataLock);
return FALSE;
}
return TRUE;
}
BOOL DestroyRWLock(RWLock *pLock)
{
DWORD result = WaitForSingleObject(pLock->hDataLock, 0);
if (result == WAIT_TIMEOUT)
return FatalError("DestroyRWLock - Can't destroy object, it's locked!");
CloseHandle(pLock->hMutex);
CloseHandle(pLock->hDataLock);
return TRUE;
}
BOOL AcquireReadLock(RWLock *pLock)
{
BOOL result = TRUE;
if (!MyWaitForSingleObject(pLock->hMutex))
return FALSE;
if(++pLock->nReaderCount == 1)
result = MyWaitForSingleObject(pLock->hDataLock);
ReleaseMutex(pLock->hMutex);
return result;
}
BOOL ReleaseReadLock(RWLock *pLock)
{
int result;
LONG lPrevCount;
if (!MyWaitForSingleObject(pLock->hMutex))
return FALSE;
if (--pLock->nReaderCount == 0)
result = ReleaseSemaphore(pLock->hDataLock, 1, &lPrevCount);
ReleaseMutex(pLock->hMutex);
return result;
}
BOOL AcquireWriteLock(RWLock *pLock)
{
return MyWaitForSingleObject(pLock->hDataLock);
}
BOOL ReleaseWriteLock(RWLock *pLock)
{
int result;
LONG lPrevCount;
result = ReleaseSemaphore(pLock->hDataLock, 1, &lPrevCount);
if (lPrevCount != 0)
FatalError("ReleaseWriteLock - Semaphore was not locked!");
return result;
}
BOOL ReadOK(RWLock *pLock)
{
// This check is not perfect, because we
// do not know for sure if we are one of
// the readers.
return (pLock->nReaderCount > 0);
}
BOOL WriteOK(RWLock *pLock)
{
DWORD result;
// The first reader may be waiting in the mutex,
// but any more than that is an error.
if (pLock->nReaderCount > 1)
return FALSE;
// This check is not perfect, because we
// do not know for sure if this thread was
// the one that had the semaphore locked.
result = WaitForSingleObject(pLock->hDataLock, 0);
if (result == WAIT_TIMEOUT)
return TRUE;
// a count is kept, which was incremented in Wait.
result = ReleaseSemaphore(pLock->hDataLock, 1, NULL);
if (result == FALSE)
FatalError("WriteOK - ReleaseSemaphore failed");
return FALSE;
}
///////////////////////////////////////////////////////
/*
* Error handler
*/
BOOL FatalError(char *s)
{
fprintf(stdout, "%s\n", s);
// Comment out exit() to prevent termination
exit(EXIT_FAILURE);
return FALSE;
}
代码
/*
* List.c
*
* Sample code for "Multithreading Applications in Win32"
* This is from Chapter 7, Listing 7-1
*
* Demonstrates an implementation of the
* Readers/Writers algorithm. This version
* gives preference to readers.
*/
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "ReadWrit.h"
///////////////////////////////////////////////////////
//
// Structure definition
//
typedef struct _Node
{
struct _Node *pNext;
char szBuffer[80];
} Node;
typedef struct _List
{
RWLock lock;
Node *pHead;
} List;
//
// Linked list prototypes
//
BOOL InitRWLock(RWLock *pLock);
BOOL DeleteList(List *pList);
BOOL AddHead(List *pList, Node *node);
BOOL DeleteHead(List *pList);
BOOL Insert(List *pList, Node *afterNode, Node *newNode);
Node *Next(List *pList, Node *node);
//
// Test functions prototypes
//
DWORD WINAPI LoadThreadFunc(LPVOID n);
DWORD WINAPI SearchThreadFunc(LPVOID n);
DWORD WINAPI DeleteThreadFunc(LPVOID n);
//
// Global variables
//
// This is the list we use for testing
List *gpList;
///////////////////////////////////////////////////////
List *CreateList()
{
List *pList = GlobalAlloc(GPTR, sizeof(List));
if (InitRWLock(&pList->lock) == FALSE)
{
GlobalFree(pList);
pList = NULL;
}
return pList;
}
BOOL DeleteList(List *pList)
{
AcquireWriteLock(&pList->lock);
while (DeleteHead(pList))
;
ReleaseWriteLock(&pList->lock);
DestroyRWLock(&gpList->lock);
GlobalFree(pList);
return TRUE;
}
BOOL AddHead(List *pList, Node *pNode)
{
if (!WriteOK(&pList->lock))
return FatalError("AddHead - not allowed to write!");
pNode->pNext = pList->pHead;
pList->pHead = pNode;
}
BOOL DeleteHead(List *pList)
{
Node *pNode;
if (!WriteOK(&pList->lock))
return FatalError("AddHead - not allowed to write!");
if (pList->pHead == NULL)
return FALSE;
pNode = pList->pHead->pNext;
GlobalFree(pList->pHead);
pList->pHead = pNode;
return TRUE;
}
BOOL Insert(List *pList, Node *afterNode, Node *newNode)
{
if (!WriteOK(&pList->lock))
return FatalError("Insert - not allowed to write!");
if (afterNode == NULL)
{
AddHead(pList, newNode);
}
else
{
newNode->pNext = afterNode->pNext;
afterNode->pNext = newNode;
}
}
Node *Next(List *pList, Node *pNode)
{
if (!ReadOK(&pList->lock))
{
FatalError("Next - Not allowed to read!");
return NULL;
}
if (pNode == NULL)
return pList->pHead;
else
return pNode->pNext;
}
///////////////////////////////////////////////////////
DWORD WINAPI ThreadFunc(LPVOID);
int main()
{
HANDLE hThrds[4];
int slot = 0;
int rc;
int nThreadCount = 0;
DWORD dwThreadId;
gpList = CreateList();
if (!gpList)
FatalError("main - List creation failed!");
hThrds[nThreadCount++] = CreateThread(NULL,
0, LoadThreadFunc, 0, 0, &dwThreadId );
hThrds[nThreadCount++] = CreateThread(NULL,
0, SearchThreadFunc, (LPVOID)"pNode", 0, &dwThreadId );
hThrds[nThreadCount++] = CreateThread(NULL,
0, SearchThreadFunc, (LPVOID)"pList", 0, &dwThreadId );
hThrds[nThreadCount++] = CreateThread(NULL,
0, DeleteThreadFunc, 0, 0, &dwThreadId );
/* Now wait for all threads to terminate */
rc = WaitForMultipleObjects(
nThreadCount,
hThrds,
TRUE,
INFINITE );
for (slot=0; slot<nThreadCount; slot++)
CloseHandle(hThrds[slot]);
printf("\nProgram finished.\n");
DeleteList(gpList);
return EXIT_SUCCESS;
}
/*
* Slowly load the contents of "List.c" into the
* linked list.
*/
DWORD WINAPI LoadThreadFunc(LPVOID n)
{
int nBatchCount;
Node *pNode;
FILE* fp = fopen("List.c", "r");
if (!fp)
{
fprintf(stderr, "ReadWrit.c not found\n");
exit(EXIT_FAILURE);
}
pNode = GlobalAlloc(GPTR, sizeof(Node));
nBatchCount = (rand() % 10) + 2;
AcquireWriteLock(&gpList->lock);
while (fgets(pNode->szBuffer, sizeof(Node), fp))
{
AddHead(gpList, pNode);
// Try not to hog the lock
if (--nBatchCount == 0)
{
ReleaseWriteLock(&gpList->lock);
Sleep(rand() % 5);
nBatchCount = (rand() % 10) + 2;
AcquireWriteLock(&gpList->lock);
}
pNode = GlobalAlloc(GPTR, sizeof(Node));
}
ReleaseWriteLock(&gpList->lock);
return 0;
}
/*
* Every so often, walked the linked list
* and figure out how many lines one string
* appears (given as the startup param)
*/
DWORD WINAPI SearchThreadFunc(LPVOID n)
{
int i;
char *szSearch = (char *)n;
for (i=0; i<20; i++)
{
int nFoundCount = 0;
Node *next = NULL;
AcquireReadLock(&gpList->lock);
next = Next(gpList, next);
while (next)
{
if (strstr(next->szBuffer, szSearch))
nFoundCount++;
next = Next(gpList, next);
}
ReleaseReadLock(&gpList->lock);
printf("Found %d lines with '%s'\n", nFoundCount, szSearch);
Sleep((rand() % 30));
}
return 0;
}
/*
* Every so often, delete some entries in the list.
*/
DWORD WINAPI DeleteThreadFunc(LPVOID n)
{
int i;
for (i=0; i<100; i++)
{
Sleep(1);
AcquireWriteLock(&gpList->lock);
DeleteHead(gpList);
DeleteHead(gpList);
DeleteHead(gpList);
ReleaseWriteLock(&gpList->lock);
}
return 0;
}
* List.c
*
* Sample code for "Multithreading Applications in Win32"
* This is from Chapter 7, Listing 7-1
*
* Demonstrates an implementation of the
* Readers/Writers algorithm. This version
* gives preference to readers.
*/
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "ReadWrit.h"
///////////////////////////////////////////////////////
//
// Structure definition
//
typedef struct _Node
{
struct _Node *pNext;
char szBuffer[80];
} Node;
typedef struct _List
{
RWLock lock;
Node *pHead;
} List;
//
// Linked list prototypes
//
BOOL InitRWLock(RWLock *pLock);
BOOL DeleteList(List *pList);
BOOL AddHead(List *pList, Node *node);
BOOL DeleteHead(List *pList);
BOOL Insert(List *pList, Node *afterNode, Node *newNode);
Node *Next(List *pList, Node *node);
//
// Test functions prototypes
//
DWORD WINAPI LoadThreadFunc(LPVOID n);
DWORD WINAPI SearchThreadFunc(LPVOID n);
DWORD WINAPI DeleteThreadFunc(LPVOID n);
//
// Global variables
//
// This is the list we use for testing
List *gpList;
///////////////////////////////////////////////////////
List *CreateList()
{
List *pList = GlobalAlloc(GPTR, sizeof(List));
if (InitRWLock(&pList->lock) == FALSE)
{
GlobalFree(pList);
pList = NULL;
}
return pList;
}
BOOL DeleteList(List *pList)
{
AcquireWriteLock(&pList->lock);
while (DeleteHead(pList))
;
ReleaseWriteLock(&pList->lock);
DestroyRWLock(&gpList->lock);
GlobalFree(pList);
return TRUE;
}
BOOL AddHead(List *pList, Node *pNode)
{
if (!WriteOK(&pList->lock))
return FatalError("AddHead - not allowed to write!");
pNode->pNext = pList->pHead;
pList->pHead = pNode;
}
BOOL DeleteHead(List *pList)
{
Node *pNode;
if (!WriteOK(&pList->lock))
return FatalError("AddHead - not allowed to write!");
if (pList->pHead == NULL)
return FALSE;
pNode = pList->pHead->pNext;
GlobalFree(pList->pHead);
pList->pHead = pNode;
return TRUE;
}
BOOL Insert(List *pList, Node *afterNode, Node *newNode)
{
if (!WriteOK(&pList->lock))
return FatalError("Insert - not allowed to write!");
if (afterNode == NULL)
{
AddHead(pList, newNode);
}
else
{
newNode->pNext = afterNode->pNext;
afterNode->pNext = newNode;
}
}
Node *Next(List *pList, Node *pNode)
{
if (!ReadOK(&pList->lock))
{
FatalError("Next - Not allowed to read!");
return NULL;
}
if (pNode == NULL)
return pList->pHead;
else
return pNode->pNext;
}
///////////////////////////////////////////////////////
DWORD WINAPI ThreadFunc(LPVOID);
int main()
{
HANDLE hThrds[4];
int slot = 0;
int rc;
int nThreadCount = 0;
DWORD dwThreadId;
gpList = CreateList();
if (!gpList)
FatalError("main - List creation failed!");
hThrds[nThreadCount++] = CreateThread(NULL,
0, LoadThreadFunc, 0, 0, &dwThreadId );
hThrds[nThreadCount++] = CreateThread(NULL,
0, SearchThreadFunc, (LPVOID)"pNode", 0, &dwThreadId );
hThrds[nThreadCount++] = CreateThread(NULL,
0, SearchThreadFunc, (LPVOID)"pList", 0, &dwThreadId );
hThrds[nThreadCount++] = CreateThread(NULL,
0, DeleteThreadFunc, 0, 0, &dwThreadId );
/* Now wait for all threads to terminate */
rc = WaitForMultipleObjects(
nThreadCount,
hThrds,
TRUE,
INFINITE );
for (slot=0; slot<nThreadCount; slot++)
CloseHandle(hThrds[slot]);
printf("\nProgram finished.\n");
DeleteList(gpList);
return EXIT_SUCCESS;
}
/*
* Slowly load the contents of "List.c" into the
* linked list.
*/
DWORD WINAPI LoadThreadFunc(LPVOID n)
{
int nBatchCount;
Node *pNode;
FILE* fp = fopen("List.c", "r");
if (!fp)
{
fprintf(stderr, "ReadWrit.c not found\n");
exit(EXIT_FAILURE);
}
pNode = GlobalAlloc(GPTR, sizeof(Node));
nBatchCount = (rand() % 10) + 2;
AcquireWriteLock(&gpList->lock);
while (fgets(pNode->szBuffer, sizeof(Node), fp))
{
AddHead(gpList, pNode);
// Try not to hog the lock
if (--nBatchCount == 0)
{
ReleaseWriteLock(&gpList->lock);
Sleep(rand() % 5);
nBatchCount = (rand() % 10) + 2;
AcquireWriteLock(&gpList->lock);
}
pNode = GlobalAlloc(GPTR, sizeof(Node));
}
ReleaseWriteLock(&gpList->lock);
return 0;
}
/*
* Every so often, walked the linked list
* and figure out how many lines one string
* appears (given as the startup param)
*/
DWORD WINAPI SearchThreadFunc(LPVOID n)
{
int i;
char *szSearch = (char *)n;
for (i=0; i<20; i++)
{
int nFoundCount = 0;
Node *next = NULL;
AcquireReadLock(&gpList->lock);
next = Next(gpList, next);
while (next)
{
if (strstr(next->szBuffer, szSearch))
nFoundCount++;
next = Next(gpList, next);
}
ReleaseReadLock(&gpList->lock);
printf("Found %d lines with '%s'\n", nFoundCount, szSearch);
Sleep((rand() % 30));
}
return 0;
}
/*
* Every so often, delete some entries in the list.
*/
DWORD WINAPI DeleteThreadFunc(LPVOID n)
{
int i;
for (i=0; i<100; i++)
{
Sleep(1);
AcquireWriteLock(&gpList->lock);
DeleteHead(gpList);
DeleteHead(gpList);
DeleteHead(gpList);
ReleaseWriteLock(&gpList->lock);
}
return 0;
}
od