前不久刚完成了一个IOCP的服务器现在碰到一个问题就是该IOCP会操作很多的文件 (文件个数可能会是上万个) 其中可能会有100来个 更新会很频繁
然后做2种操作 一个是读 一个是创建文件 (已存在的文件可能会被重新创建)现在问题就是 如果只用一个临界区做锁 那么 当多个线程都为读的时候 性能又会很差 因为不管是读多个文件还是读同一个文件 其实都是可以同时去读的 并不需要去锁但更不可能为每个文件都去创建锁 如果取消锁 拼运气(读的频率会很高 写的频率相对会低很多) 那又感觉好像太不厚道了...
所以向大家请教与讨论 寻求更好的解决方案
然后做2种操作 一个是读 一个是创建文件 (已存在的文件可能会被重新创建)现在问题就是 如果只用一个临界区做锁 那么 当多个线程都为读的时候 性能又会很差 因为不管是读多个文件还是读同一个文件 其实都是可以同时去读的 并不需要去锁但更不可能为每个文件都去创建锁 如果取消锁 拼运气(读的频率会很高 写的频率相对会低很多) 那又感觉好像太不厚道了...
所以向大家请教与讨论 寻求更好的解决方案
你的看法是最明白的 目前我也是没找到很完美的办法 因为有几个重点问题
1、是大量的文件 如果上万的文件 因此我不想建立上万个临界区
2、多线程也可能操作的时候(不管读还是写) 因为文件多 可能读写的重复率相对较低 只用一个临界区 肯定效率低 我也想的到允许并发读 写的时候锁
正如2楼所讲 Windows没有提供现成的方法 你来展示一下完整的思路和解决方案吧 可以用伪代码描述一下
总之,除非你放弃读共享(即:不允许多个线程同时读同一个文件),否则的话,最合理的方式是建立一个专门的调度线程,用来控制所有其它线程在文件操作上的同步和互斥。单靠线程本身自发控制,就算你现在能彻底把这团乱麻想清楚,回头过几天你自己就又看不懂了。其实,文件的读写共享问题,操作系统本身就可以处理得很好,只不过当共享冲突的时候,CreateFile并不等待,而是直接返回错误。而你现在的问题无非就是要解决这个等待吗?那你弄个循环,只要发生共享冲突的时候Sleep(50)就是了,虽然粗糙点,不过这实在是最简单的解决方案了。
* 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;
}
测试程序/*
* 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;
}