For the first: you should know what you want to lock. At most times you should lock resources that you would use at once. For instance an output file stream to prevent the mixed output of two threads. I.e. Thread-A writes AAAA and Thread-B writes BBBB this would possible the unlocked output of ABABABAB. Thats not what you want therefore you should lock the output. The aim is to commit the result of an operation at once. The output resource can be very different. This can be a file stream, any other stream, databases, services, system functions and so on. To minimize the lock time you should lock the resource immediate when you open it and unlock when the resource is closed.
The following example illustrates the locking of a output file usage by different threads. All the threads write the thread result to the output file.
Most of the code is decoration (but works) to illustrate a multithread processing of files.
The most important classes are: CDataLock and CMainObject.
The CMainObject executes a task on a file and prints the result to the output file with locking it by CDataLock.
In most cases the usage of mutex objects are the best choice. They can be shared among threads and proccesses too (named mutex objects).
This example walks over all directories in drive C:\ and counts all bytes 'A' in the found header (*.h) files. The results are written to C:\Temp\log.txt (hint: enshure the path exist).
#pragma once
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#ifdef _DEBUG
#include <crtdbg.h>
#define START() _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
#else
#define START()
#endif
class CDataLock
{
public:
typedef HANDLE LOCKOBJECT;
public:
CDataLock(LOCKOBJECT& lockobject) { _hlock=lockobject; if(_hlock) WaitForSingleObject(_hlock,INFINITE); }
~CDataLock() { if(_hlock) ReleaseMutex(_hlock); }
static void Init(LOCKOBJECT& lockobject) { lockobject = CreateMutex(0,0,0); }
static void Exit(LOCKOBJECT& lockobject) { if(lockobject) CloseHandle(lockobject); }
private:
LOCKOBJECT _hlock;
};
class CMainObject
{
enum{ SEARCH_BYTE = 'A', };
public:
CMainObject(const TCHAR* infile,const TCHAR* outfile,CDataLock::LOCKOBJECT& lock);
~CMainObject();
public:
static LONG FAR PASCAL __fnThread(void* p){ return p?((CMainObject*)p)->Thread():0; }
protected:
virtual long Thread();
void Print(const TCHAR* format,...);
private:
CDataLock::LOCKOBJECT _lock;
TCHAR* _infile;
TCHAR* _outfile;
};
CMainObject::CMainObject(const TCHAR* infile,const TCHAR* outfile,CDataLock::LOCKOBJECT& lock):_lock(lock)
{
_infile = _tcsdup(infile?infile:__TEXT(""));
_outfile = _tcsdup(outfile?outfile:__TEXT(""));
}
CMainObject::~CMainObject()
{
if(_infile) free(_infile);
if(_outfile) free(_outfile);
}
long CMainObject::Thread()
{
HANDLE h = ::CreateFile(_infile,GENERIC_READ,0,0,OPEN_EXISTING,0,0);
if(INVALID_HANDLE_VALUE!=h)
{
unsigned char readbuff[0x1000];
unsigned long readlen,ix;
unsigned int count = 0;
while(::ReadFile(h,readbuff,sizeof(readbuff),&readlen,0) && (0<readlen))
{
for(ix=0;ix<readlen;ix++)
{
if(SEARCH_BYTE==readbuff[ix])
++count;
}
}
CloseHandle(h);
Print(__TEXT("%c - %i times found in: %s\r\n"),(TCHAR)SEARCH_BYTE,count,_infile);
}
return 0;
}
void CMainObject::Print(const TCHAR* format,...)
{
CDataLock l(_lock);
HANDLE h = ::CreateFile(_outfile,GENERIC_WRITE,0,0,OPEN_ALWAYS,0,0);
if(INVALID_HANDLE_VALUE!=h)
{
unsigned long w = 0;
va_list val;
TCHAR f[0x1000];
int l;
va_start(val,format);
l = _vsntprintf_s(f,sizeof(f)/sizeof(f[0]),_TRUNCATE,format,val);
va_end(val);
if(0==SetFilePointer(h,0,0,FILE_END))
{
if(sizeof(short)==sizeof(TCHAR))
{
unsigned short unicode = 0xFeFF;
WriteFile(h,(void*)&unicode,2,&w,0);
}
}
WriteFile(h,(void*)f,l*sizeof(TCHAR),&w,0);
CloseHandle(h);
}
}
class CThreadArray
{
public:
CThreadArray(const TCHAR* outfile);
~CThreadArray();
void Add(const TCHAR* infile);
private:
unsigned int NextFree();
unsigned int Wait(const int waitall,const unsigned long timeout);
private:
enum{ MAX_THREADS=MAXIMUM_WAIT_OBJECTS , };
HANDLE _ahThreads[MAX_THREADS];
CMainObject* _apThreads[MAX_THREADS];
unsigned int _nhThreads;
const TCHAR* _outfile;
CDataLock::LOCKOBJECT _lock;
};
CThreadArray::CThreadArray(const TCHAR* outfile)
{
CDataLock::Init(_lock);
_outfile = outfile;
_nhThreads = 0;
::DeleteFile(_outfile);
}
CThreadArray::~CThreadArray()
{
unsigned int ix;
Wait(1,INFINITE);
for(ix=0;ix<_nhThreads;ix++)
{
delete _apThreads[ix];
}
CDataLock::Exit(_lock);
}
unsigned int CThreadArray::Wait(const int waitall,const unsigned long timeout)
{
unsigned int wr = WaitForMultipleObjects(_nhThreads,_ahThreads,waitall,timeout);
return (wr>=WAIT_OBJECT_0) && (wr<(WAIT_OBJECT_0+_nhThreads))?wr-WAIT_OBJECT_0:-1;
}
unsigned int CThreadArray::NextFree()
{
unsigned int wr;
unsigned int ix = 0;
if(_nhThreads<MAX_THREADS) return _nhThreads++;
while(0<_nhThreads)
{
wr = Wait(0,INFINITE);
if(wr<_nhThreads)
{
ix = wr;
break;
}
}
if(ix<_nhThreads)
{
delete _apThreads[ix];
_apThreads[ix] = 0;
_ahThreads[ix] = 0;
}
return ix;
}
void CThreadArray::Add(const TCHAR* infile)
{
unsigned int ix = NextFree();
unsigned long tid;
_apThreads[ix] = new CMainObject(infile,_outfile,_lock);
_ahThreads[ix] = CreateThread(0,0,(LPTHREAD_START_ROUTINE)CMainObject::__fnThread,_apThreads[ix],0,&tid);
#ifdef _DEBUG
_tprintf(__TEXT("%s\r\n"),infile);
#endif
}
class vWalkFs
{
public:
virtual int Enter(const TCHAR* filename,WIN32_FIND_DATA& fd) = 0;
virtual int Leave(const TCHAR* filename,WIN32_FIND_DATA& fd) = 0;
static void Walk(const TCHAR* path,const TCHAR* filter,vWalkFs& walk);
private:
static void Walk(TCHAR* path,const unsigned int cpath,const unsigned int lpath,const TCHAR* filter,const unsigned int lfilter,vWalkFs& walk);
};
void vWalkFs::Walk(const TCHAR* path,const TCHAR* filter,vWalkFs& walk)
{
TCHAR full[0x1000];
_tcscpy_s(full,sizeof(full)/sizeof(full[0]),path);
Walk(full,sizeof(full)/sizeof(full[0]),_tcslen(full),filter,_tcslen(filter),walk);
}
void vWalkFs::Walk(TCHAR* path,const unsigned int cpath,const unsigned int lpath,const TCHAR* filter,const unsigned int lfilter,vWalkFs& walk)
{
typedef struct { static int dot(const TCHAR* s){ for(;'.'==*s;s++); return 0==*s?1:0; } } is;
HANDLE hf;
WIN32_FIND_DATA fd;
unsigned int lf,ll = lpath;
if(!lpath) return;
if((lfilter+lpath+1)>cpath) return;
if('\\'!=path[ll-1]) path[ll++] = '\\';
_tcscpy_s(path+ll,cpath-ll,filter);
hf = FindFirstFile(path,&fd);
if(INVALID_HANDLE_VALUE!=hf)
{
do
{
if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
lf = _tcslen(fd.cFileName);
if((ll+lf+1)<cpath)
{
_tcscpy_s(path+ll,cpath-ll,fd.cFileName);
if(!walk.Enter(path,fd)) break;
walk.Leave(path,fd);
}
}
} while(FindNextFile(hf,&fd));
FindClose(hf);
}
_tcscpy_s(path+ll,cpath-ll,__TEXT("*.*"));
hf = FindFirstFile(path,&fd);
if(INVALID_HANDLE_VALUE!=hf)
{
do
{
if((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !is::dot(fd.cFileName))
{
lf = _tcslen(fd.cFileName);
if((ll+lf+1)<cpath)
{
_tcscpy_s(path+ll,cpath-ll,fd.cFileName);
if(!walk.Enter(path,fd)) break;
Walk(path,cpath,ll+lf,filter,lfilter,walk);
walk.Leave(path,fd);
}
}
} while(FindNextFile(hf,&fd));
FindClose(hf);
}
path[lpath] = 0;
}
class CWalkFs : public vWalkFs
{
public:
virtual int Enter(const TCHAR* filename,WIN32_FIND_DATA& fd)
{
if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
_ta.Add(filename);
return 1;
}
virtual int Leave(const TCHAR* filename,WIN32_FIND_DATA& fd)
{
return 1;
}
CWalkFs(CThreadArray& ta):_ta(ta){}
private:
CThreadArray& _ta;
};
int _tmain(int argc, _TCHAR* argv[])
{
START()
CThreadArray ta(__TEXT("C:\\Temp\\log.txt"));
CWalkFs walk(ta);
vWalkFs::Walk(__TEXT("C:\\"),__TEXT("*.h"),walk);
_tprintf(__TEXT("<key>")); _gettch();
return 0;
}
Good luck and best regards.