1.把锁定搬到更高的层次
void AddLineItems(List *pList)
{
Node node;
while()
{
getLineItem(&item);
AddHead(pList,&node);
}
}
void AddHead(List *pList,Node *pNode)
{
EnterCriticalSection(&pList->critical_sec);
pNode->next=pList->head;
pList->head=pNode;
LeaveCriticalSection(&pList->critical_sec);
}
搬到更高的层次
void AddLineItems(List *pList)
{
Node node;
EnterCriticalSection(&pList->critical_sec);
while()
{
getLineItem(&item);
AddHead(pList,&node);
}
LeaveCriticalSection(&pList->critical_sec);
}
void AddHead(List *pList,Node *pNode)
{
pNode->next=pList->head;
pList->head=pNode;
}
这样的模型有助于windows程序,因为其线程是事件驱动模式,而且常常返回到主消息循环中。
2.读写锁
排他锁看起来不错,但是它会强迫所有的存取操作都按照先后次序来,而多线程的好处也就流失了。这种情况下最好用单一线程。
为Reader锁定
Lock(ReaderMutex)
ReadCount=ReadCount+1;
if(ReadCount==0)
Unlock(DataSemaphore)
Unlock(ReaderMutex)
为Reader解除锁定
Lock(ReaderMutex)
ReadCount=ReadCount-1;
if(ReadCount==0)
Unlock(DataSemaphore)
Unlock(ReaderMutex)
为Writer锁定
Lock(DataSemaphore)
为Writer解除锁定
Unlock(DataSemaphore)
实现Reader/Writer的数据结构
ReadWrit.h
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);
读者与写者算法:
#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; }
主文件,多读者和写者对链表的操作
#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; }