多线程使用内存映射的问题

lbiboy 2013-08-06 09:57:19
我写的程序主要功能是生成多个线程,每个线程函数均相同,包含一个CopyFile与一个CompareFile函数。两个函数均用到内存映射,问题主要出在CompareFile函数内。这个函数工作过程如下:CompareFile(CString strFilePathSrc, CString strFilepathDes,int nindex)
strFilePathSrc是一个源文件名,这个文件用createfile打开,并用CreateFileMapping映射整个文件,然后再用这个得到的句柄创建一个视图(此相关的访问权限均是只读,且共享读)。
strFilepathDes这个是要比较的目标文件名,也用createfile打开,生成一个BYTE Buff[64*1024]的临时变量,将目标文件读入此Buff,并计算其速度。
最后将源文件得到的视图基地址pVoidSrc与Buff的地址,做为memcmp函数的参数,进行比较。
主要思路就这些。

以下是这个函数的代码:
int CAutoCopyCompareDoc::CompareFile(CString strFilePathSrc, CString strFilepathDes,int nindex) //nindex为显示用到的变量,对其无影响
{
//以下是取得一些要显示用到的指针,对讨论的内容无影响
POSITION Position=GetFirstViewPosition();
CAutoCopyCompareView *pView=(CAutoCopyCompareView *)GetNextView(Position);
ItemInfo *pItemInfo;
pItemInfo=(ItemInfo *)pView->GetListCtrl().GetItemData(nindex);

//初始化一些变量
CString strDrive=strFilepathDes;
strDrive=strDrive.Left(3);
HANDLE hFileSrc=INVALID_HANDLE_VALUE;
HANDLE hFileDes=INVALID_HANDLE_VALUE;
HANDLE hFileMapSrc=NULL;
//HANDLE hFileMapDes=NULL;
LPVOID pVoidSrc=NULL;
//LPVOID pVoidDes=NULL;
int retnumber=0; //函数返回值

//以只读且共享读的方式打开源文件
hFileSrc=::CreateFile(strFilePathSrc,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if(hFileSrc==INVALID_HANDLE_VALUE)
{
DWORD dw=::GetLastError();
LogError(dw,strFilepathDes,TEXT("Compare File"),strDrive);
goto clear;
}


//以只读且共享读的方式打开源文件
hFileDes=::CreateFile(strFilepathDes,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if(hFileDes==INVALID_HANDLE_VALUE)
{
DWORD dw=::GetLastError();
LogError(dw,strFilepathDes,TEXT("Compare File"),strDrive);
goto clear;
}
//两者大小不等,则肯定有错
LARGE_INTEGER FileSizeSrc;
::GetFileSizeEx(hFileSrc,&FileSizeSrc);
LARGE_INTEGER FileSizeDes;
::GetFileSizeEx(hFileDes,&FileSizeDes);
if(FileSizeDes.QuadPart!=FileSizeSrc.QuadPart)
{
DWORD dw=::GetLastError();
LogError(dw,strFilepathDes,TEXT("Compare File size not equal"),strDrive);
goto clear;

}

//创建内存映射文件
hFileMapSrc=::CreateFileMapping(hFileSrc,NULL,PAGE_READONLY,0,0,NULL);
if(hFileMapSrc==NULL/*||hFileMapDes==NULL*/)
{
DWORD dw=::GetLastError();
LogError(dw,strFilepathDes,TEXT("Compare File"),strDrive);
goto clear;
}

LARGE_INTEGER start;
LONGLONG bytes=64*1024; //一次映射64KB
LONGLONG i=1;

//以下是分段创建一个视图,视图最大64KB
do
{
if(m_bCancle) //用户退出
break;

start.QuadPart=(i-1)*bytes;
if(bytes>=FileSizeSrc.QuadPart-start.QuadPart) //剩下的末映射的视图如小于64KB时,则让bytes=剩下的数据大小
bytes=FileSizeSrc.QuadPart-start.QuadPart;
BYTE Buff[64*1024]={0}; //目标文件要读入的Buff

//计时
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;// 获得初始值
DWORD NumbersBytesWritten=0;
int retreturn=0;
retreturn=::ReadFile(hFileDes,Buff,bytes,&NumbersBytesWritten,NULL); //读U盘文件内的数据
if(retreturn==0||NumbersBytesWritten!=bytes) //读入出错
{
DWORD dw=::GetLastError();
CString strErrorType=TEXT("Compare File");
LogError(dw,strFilePathSrc,strErrorType,strDrive); //向日志写入出错信息
}
QueryPerformanceCounter(&litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒

double Temp1=bytes/dfTim;
double Temp2=Temp1/1024;
double m_nSpeed=Temp2/1024; //速度 单位MB/S
pItemInfo->nSpeed=m_nSpeed; //将速度值传进一个结构去显示出来


pVoidSrc=MapViewOfFile(hFileMapSrc,FILE_MAP_READ,start.HighPart,start.LowPart,bytes); //映射一个源文件的视图

//以下是调试信息,输出各个线程的pVoidSrc地址
DWORD nThread=::GetCurrentThreadId();
CString strTrace;
strTrace.Format(TEXT("Thread %d pVoidSrc=%x\n"),nThread,pVoidSrc);
TRACE(strTrace);


DWORD dw=::GetLastError();
if(pVoidSrc==NULLdw!=0) //内存映射文件创建成功
{

LogError(dw,strFilepathDes,TEXT("Compare File"),strFilepathDes);
goto clear;
}

int ret=0;
ret=memcmp(pVoidSrc,Buff,bytes); //比较数据
if(ret!=0) //匹配有错
{
DWORD dw=::GetLastError();
LogError(dw,strFilePathSrc,TEXT("Compare File"),strFilepathDes);
goto clear;
}
::UnmapViewOfFile(pVoidSrc);
i++;


}while(start.QuadPart + bytes<FileSizeSrc.QuadPart); //当视图的起点值加上bytes仍小于源文件的大小时则继续循环

retnumber=1; //函数执行成功

clear: //清理工作
{
if(hFileSrc!=INVALID_HANDLE_VALUE)
::CloseHandle(hFileSrc);
if(hFileDes!=INVALID_HANDLE_VALUE)
::CloseHandle(hFileDes);
if(hFileMapSrc!=NULL)
::CloseHandle(hFileMapSrc);
if(pVoidSrc!=NULL)
::UnmapViewOfFile(pVoidSrc);


}

return retnumber;
}


这个函数只在一个线程内执行没有问题,如若在多个线程内执行,则发生一个0xc000005异常,停在这段代码处
ret=memcmp(pVoidSrc,Buff,bytes); //比较数据
显示pVoidSrc后面的某个地址访问冲突。(包括其开始地址)

思考:
1、当一个线程创建了一个视图,另一个线程创建了相同的一个视图,且先于那个线程创建,这时要UnmapViewOfFile。如果Unmap掉的话第一个线程对pVoidSrc的memcmp会引起内存访问异常吗?
2、如果有异常的话,那要加入同步了?但加入同步之后,内存映射便没优势了,也就没必要再用了,还有其他解决方法吗?
3、这个线程还有一个CopyFile函数,用到的是相同的技术。CopyFile(CString strFilePathSrc, CString strFilePathDes,int nindex)
主要过程如下:
1、用只读且共享读方式打开strFilePathSrc源文件,用CreateFileMapping映射,再用MapViewOfFile创建一个视图,得到一个基地址pVoid
2、用可读可写不共享方式打开strFilePathDes目标文件,用基地址pVoid做为writefile参数向目标文件写入数据

这里面也会有多个线程多次创建视图的过程,为什么这个函数却没异常呢?

以上是我的全部疑问,望有研究过内存映射的指导一二,不胜感激。
...全文
265 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
lbiboy 2013-08-06
  • 打赏
  • 举报
回复
附上我一个异常出现时的图片
lbiboy 2013-08-06
  • 打赏
  • 举报
回复
整个程序的功能是这样的: 有多个U盘,一般至少是4个,将电脑里的文件同时拷贝到U盘内,再比较。线程函数均是一样的,都包含拷贝文件函数,匹配文件函数。
lbiboy 2013-08-06
  • 打赏
  • 举报
回复
谢谢,我找到原因了。
赵4老师 2013-08-06
  • 打赏
  • 举报
回复
仅供参考
//循环向a函数每次发送200个字节长度(这个是固定的)的buffer,
//a函数中需要将循环传进来的buffer,组成240字节(也是固定的)的新buffer进行处理,
//在处理的时候每次从新buffer中取两个字节打印
#ifdef WIN32
    #pragma warning(disable:4996)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
    #include <windows.h>
    #include <process.h>
    #include <io.h>
    #define  MYVOID             void
    #define  vsnprintf          _vsnprintf
#else
    #include <unistd.h>
    #include <sys/time.h>
    #include <pthread.h>
    #define  CRITICAL_SECTION   pthread_mutex_t
    #define  MYVOID             void *
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
    EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
    LeaveCriticalSection(l);
}
void sleep_ms(int ms) {
    Sleep(ms);
}
#else
void Lock(CRITICAL_SECTION *l) {
    pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
    pthread_mutex_unlock(l);
}
void sleep_ms(int ms) {
    usleep(ms*1000);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
    struct tm *now;
    struct timeb tb;

    if (NULL==pszFmt||0==pszFmt[0]) return;
    vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,logstr);
    flog=fopen(logfilename1,"a");
    if (NULL!=flog) {
        fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
        if (ftell(flog)>MAXLOGSIZE) {
            fclose(flog);
            if (rename(logfilename1,logfilename2)) {
                remove(logfilename2);
                rename(logfilename1,logfilename2);
            }
        } else {
            fclose(flog);
        }
    }
}
void Log(const char *pszFmt,...) {
    va_list argp;

    Lock(&cs_log);
    va_start(argp,pszFmt);
    LogV(pszFmt,argp);
    va_end(argp);
    Unlock(&cs_log);
}
//Log}
#define ASIZE    200
#define BSIZE    240
#define CSIZE      2
char Abuf[ASIZE];
char Cbuf[CSIZE];
CRITICAL_SECTION cs_HEX ;
CRITICAL_SECTION cs_BBB ;
struct FIFO_BUFFER {
    int  head;
    int  tail;
    int  size;
    char data[BSIZE];
} BBB;
int No_Loop=0;
void HexDump(int cn,char *buf,int len) {
    int i,j,k;
    char binstr[80];

    Lock(&cs_HEX);
    for (i=0;i<len;i++) {
        if (0==(i%16)) {
            sprintf(binstr,"%03d %04x -",cn,i);
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
        } else if (15==(i%16)) {
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
            sprintf(binstr,"%s  ",binstr);
            for (j=i-15;j<=i;j++) {
                sprintf(binstr,"%s%c",binstr,('!'<buf[j]&&buf[j]<='~')?buf[j]:'.');
            }
            Log("%s\n",binstr);
        } else {
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
        }
    }
    if (0!=(i%16)) {
        k=16-(i%16);
        for (j=0;j<k;j++) {
            sprintf(binstr,"%s   ",binstr);
        }
        sprintf(binstr,"%s  ",binstr);
        k=16-k;
        for (j=i-k;j<i;j++) {
            sprintf(binstr,"%s%c",binstr,('!'<buf[j]&&buf[j]<='~')?buf[j]:'.');
        }
        Log("%s\n",binstr);
    }
    Unlock(&cs_HEX);
}
int GetFromRBuf(int cn,CRITICAL_SECTION *cs,FIFO_BUFFER *fbuf,char *buf,int len) {
    int lent,len1,len2;

    lent=0;
    Lock(cs);
    if (fbuf->size>=len) {
        lent=len;
        if (fbuf->head+lent>BSIZE) {
            len1=BSIZE-fbuf->head;
            memcpy(buf     ,fbuf->data+fbuf->head,len1);
            len2=lent-len1;
            memcpy(buf+len1,fbuf->data           ,len2);
            fbuf->head=len2;
        } else {
            memcpy(buf     ,fbuf->data+fbuf->head,lent);
            fbuf->head+=lent;
        }
        fbuf->size-=lent;
    }
    Unlock(cs);
    return lent;
}
MYVOID thdB(void *pcn) {
    char        *recv_buf;
    int          recv_nbytes;
    int          cn;
    int          wc;
    int          pb;

    cn=(int)pcn;
    Log("%03d thdB              thread begin...\n",cn);
    while (1) {
        sleep_ms(10);
        recv_buf=(char *)Cbuf;
        recv_nbytes=CSIZE;
        wc=0;
        while (1) {
            pb=GetFromRBuf(cn,&cs_BBB,&BBB,recv_buf,recv_nbytes);
            if (pb) {
                Log("%03d recv %d bytes\n",cn,pb);
                HexDump(cn,recv_buf,pb);
                sleep_ms(1);
            } else {
                sleep_ms(1000);
            }
            if (No_Loop) break;//
            wc++;
            if (wc>3600) Log("%03d %d==wc>3600!\n",cn,wc);
        }
        if (No_Loop) break;//
    }
#ifndef WIN32
    pthread_exit(NULL);
#endif
}
int PutToRBuf(int cn,CRITICAL_SECTION *cs,FIFO_BUFFER *fbuf,char *buf,int len) {
    int lent,len1,len2;

    Lock(cs);
    lent=len;
    if (fbuf->size+lent>BSIZE) {
        lent=BSIZE-fbuf->size;
    }
    if (fbuf->tail+lent>BSIZE) {
        len1=BSIZE-fbuf->tail;
        memcpy(fbuf->data+fbuf->tail,buf     ,len1);
        len2=lent-len1;
        memcpy(fbuf->data           ,buf+len1,len2);
        fbuf->tail=len2;
    } else {
        memcpy(fbuf->data+fbuf->tail,buf     ,lent);
        fbuf->tail+=lent;
    }
    fbuf->size+=lent;
    Unlock(cs);
    return lent;
}
MYVOID thdA(void *pcn) {
    char        *send_buf;
    int          send_nbytes;
    int          cn;
    int          wc;
    int           a;
    int          pa;

    cn=(int)pcn;
    Log("%03d thdA              thread begin...\n",cn);
    a=0;
    while (1) {
        sleep_ms(100);
        memset(Abuf,a,ASIZE);
        a=(a+1)%256;
        if (16==a) {No_Loop=1;break;}//去掉这句可以让程序一直循环直到按Ctrl+C或Ctrl+Break或当前目录下存在文件No_Loop
        send_buf=(char *)Abuf;
        send_nbytes=ASIZE;
        Log("%03d sending %d bytes\n",cn,send_nbytes);
        HexDump(cn,send_buf,send_nbytes);
        wc=0;
        while (1) {
            pa=PutToRBuf(cn,&cs_BBB,&BBB,send_buf,send_nbytes);
            Log("%03d sent %d bytes\n",cn,pa);
            HexDump(cn,send_buf,pa);
            send_buf+=pa;
            send_nbytes-=pa;
            if (send_nbytes<=0) break;//
            sleep_ms(1000);
            if (No_Loop) break;//
            wc++;
            if (wc>3600) Log("%03d %d==wc>3600!\n",cn,wc);
        }
        if (No_Loop) break;//
    }
#ifndef WIN32
    pthread_exit(NULL);
#endif
}
int main() {
#ifdef WIN32
    InitializeCriticalSection(&cs_log);
    InitializeCriticalSection(&cs_HEX );
    InitializeCriticalSection(&cs_BBB );
#else
    pthread_t threads[2];
    int threadsN;
    int rc;
    pthread_mutex_init(&cs_log,NULL);
    pthread_mutex_init(&cs_HEX,NULL);
    pthread_mutex_init(&cs_BBB,NULL);
#endif
    Log("Start===========================================================\n");

    BBB.head=0;
    BBB.tail=0;
    BBB.size=0;

#ifdef WIN32
    _beginthread((void(__cdecl *)(void *))thdA,0,(void *)1);
    _beginthread((void(__cdecl *)(void *))thdB,0,(void *)2);
#else
    threadsN=0;
    rc=pthread_create(&(threads[threadsN++]),NULL,thdA,(void *)1);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1);
    rc=pthread_create(&(threads[threadsN++]),NULL,thdB,(void *)2);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1);
#endif

    if (!access("No_Loop",0)) {
        remove("No_Loop");
        if (!access("No_Loop",0)) {
            No_Loop=1;
        }
    }
    while (1) {
        sleep_ms(1000);
        if (No_Loop) break;//
        if (!access("No_Loop",0)) {
            No_Loop=1;
        }
    }
    sleep_ms(3000);
    Log("End=============================================================\n");
#ifdef WIN32
    DeleteCriticalSection(&cs_BBB );
    DeleteCriticalSection(&cs_HEX );
    DeleteCriticalSection(&cs_log);
#else
    pthread_mutex_destroy(&cs_BBB);
    pthread_mutex_destroy(&cs_HEX);
    pthread_mutex_destroy(&cs_log);
#endif
    return 0;
}

3,881

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 其它技术问题
社区管理员
  • 其它技术问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧