【win32多线程】数据的一致性问题,即多个线程读写数据问题

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;
}


 

你可能感兴趣的:(多线程,struct,list,Semaphore,null,winapi)