VC++多线程问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VC++多线程问题相关的知识,希望对你有一定的参考价值。

大家好,我这里不知道如何在这段代码设置临界区,我的想法是一个线程调用一个sess.OpenURL(g_runURL[i]),另一个线程同时也调用sess.OpenURL(g_runURL[i])
比如线程1调用sess.OpenURL(g_runURL[1])
线程2调用sess.OpenURL(g_runURL[2])
就这样,一下是代码,如何设置临界区合适呢?

主函数有两个调用
void CReportAutoCreateDlg::OnButton1()


RecordMessage("事务日志","开始手动生成报表");
if(CreateRunUrl())

AfxBeginThread(runUrlThread,&g_runURL);
AfxBeginThread(runUrlThread,&g_runURL);

else
RecordMessage("事务日志", "手动生成报表失败,请重试");


这个是多线程函数

UINT runUrlThread(LPVOID parm)//多线程函数,用于执行URL


CInternetSession sess;
int i;
CString strMsg;
for(i = 0; i < g_runCount; i++)

g_cs.Lock();
//sess.OpenURL(g_runURL[i]);
AfxMessageBox(g_runURL[i]);

Sleep(10);
strMsg.Format("%s已调用");
RecordMessage("事务日志", strMsg);


g_cs.Unlock();

return 0;

据我分析,

1、你的sess因为每个线程都有自己的实例,所以不需要在sess上下临界区
2、目测你的目的是能够并行处理 g_runURL 数组里的地址。这样的话,应该在多个线程间共享那个循环变量i。在创建线程前将i初始化为0,然后将i的地址交给每个线程
3、需要临界区的地方在于循环里的 i++ 而不是处理过程。故临界区应该这样写:
g_cs.Lock();
for(; i < g_runCount; ++i)

int thisi = i; //注意要把在临界区内访问到的 i 的值保存在当前线程私有的变量中
g_cs.Unlock();
//这里的代码用到 i 的要换成 thisi
...//你原来的代码
g_cs.Lock();

g_cs.Unlock();追问

大哥,你说的第二步怎么做
意思是用for(i=0;i<g_runCount,i++)
AfxBeginThread(runUrlThread,&g_runURL[i]);
是这样吗

追答

我想先确定一下,你的代码运行的最终结果,是不是将 g_runURL 数组里的网址全部访问一遍?
然后 g_runCount 里面存的是什么,是不是 g_runURL数组里元素的个数

追问

对,g_runURL存的就是要访问的地址,g_runCount这个是配置文件里设定的次数,

追答int g_i; //和g_cs放一个地方
void CReportAutoCreateDlg::OnButton1() 

    
    RecordMessage("事务日志","开始手动生成报表");
    if(CreateRunUrl())
    
       g_i = 0;
       AfxBeginThread(runUrlThread, 0);
       AfxBeginThread(runUrlThread, 0);
    
    else
       RecordMessage("事务日志", "手动生成报表失败,请重试");

UINT runUrlThread(LPVOID parm)//多线程函数,用于执行URL

    CInternetSession sess;
    int i;
    CString strMsg;
    g_cs.Lock();
    for(; g_i < g_runCount; g_i++)
    
        int thisi = g_i;
        g_cs.Unlock();
        //sess.OpenURL(g_runURL[thisi]);
        AfxMessageBox(g_runURL[thisi]);
        Sleep(10);
        strMsg.Format("%s已调用");
        RecordMessage("事务日志", strMsg);
        g_cs.Lock();
    
    g_cs.Unlock();
    return 0;

追问

好像不行啊,运行出现两个一样的链接,就怕这样呢。。。。

追答

我知道了,是刚进入的那一下没有线程会执行 g_i++ 的缘故

我把代码结构调整了一下,大概长这样


int g_i; //和g_cs放一个地方 
void CReportAutoCreateDlg::OnButton1()  
 
      
    RecordMessage("事务日志","开始手动生成报表"); 
    if(CreateRunUrl()) 
     
       g_i = 0; 
       AfxBeginThread(runUrlThread, 0); 
       AfxBeginThread(runUrlThread, 0); 
     
    else 
       RecordMessage("事务日志", "手动生成报表失败,请重试"); 
 
UINT runUrlThread(LPVOID parm)//多线程函数,用于执行URL 
 
    CInternetSession sess; 
    int i; 
    CString strMsg; 
    for(;;)
     
        g_cs.Lock();
        int thisi = g_i; 
        ++g_i;
        g_cs.Unlock(); 
        if (thisi < g_runCount) 
            //sess.OpenURL(g_runURL[thisi]); 
            AfxMessageBox(g_runURL[thisi]); 
            Sleep(10); 
            strMsg.Format("%s已调用"); 
            RecordMessage("事务日志", strMsg); 
         else
            break;
    
    return 0; 

你试一下吗?

追问

谢谢您啊,运行正常了,但如何验证两个线程同时运行过呢??

老板问呢。。

追答

……你要不要把每个单独操作的开始时间和完成时间(精确到毫秒吧,秒怕不够)一起写到日志里面

这样就能看到不同操作的执行时间是重叠的了

参考技术A int curIndex[2];
void CReportAutoCreateDlg::OnButton1() 

    
    RecordMessage("事务日志","开始手动生成报表");
    if(CreateRunUrl())
    
       curIndex[0] = -1;
       curIndex[1] = -1;
       AfxBeginThread(runUrlThread,&g_runURL);
       AfxBeginThread(runUrlThread,&g_runURL);
    
    else
       RecordMessage("事务日志", "手动生成报表失败,请重试");



UINT runUrlThread(LPVOID parm)//多线程函数,用于执行URL

    
    CInternetSession sess;
    int i;
    CString strMsg;
    for(i = 0; i < g_runCount; i++)
    
        g_cs.Lock();
        if (i != curIndex[0])
            curIndex[0] = i;
        else if(i != curIndex[1])
            curIndex[1] = i;
        g_cs.Unlock();
            
        while(i != curIndex[1]) Sleep(0);
        
        //sess.OpenURL(g_runURL[i]);
        AfxMessageBox(g_runURL[i]);

Sleep(10);
        strMsg.Format("%s已调用");
        RecordMessage("事务日志", strMsg);



    return 0;

追问

这个测试了一下,发现链接会重复出现两次,和我的代码一样

追答

哦,可能是我没注意看题目了,我以为你就是要两次都同步同时执行同一个索引的URL

把 int i; 放函数外面,作为类成员也可以

int curIndex;
void CReportAutoCreateDlg::OnButton1() 

     
    RecordMessage("事务日志","开始手动生成报表");
    if(CreateRunUrl())
    
       curIndex = -1;
       AfxBeginThread(runUrlThread,&g_runURL);
       AfxBeginThread(runUrlThread,&g_runURL);
    
    else
       RecordMessage("事务日志", "手动生成报表失败,请重试");
 

 
UINT runUrlThread(LPVOID parm)//多线程函数,用于执行URL

     
    CInternetSession sess;
    int i;
    CString strMsg;
    while(true)
    
        g_cs.Lock();
        curIndex++;
        i = curIndex;
        g_cs.Unlock();
        
        if (i >= g_runCount)
            break;
         
        //sess.OpenURL(g_runURL[i]);
        AfxMessageBox(g_runURL[i]);
 
Sleep(10);
        strMsg.Format("%s已调用");
        RecordMessage("事务日志", strMsg);
 

 
    return 0;
 

追问

不对啊,也是同时出现一样的链接

C++多线程问题

怎么在C中创立多线程?最好是用dev或VC。麻烦写清楚函数的各个参量的意义

1、HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
该函数在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄,其中各参数说明如下:
lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构决定了线程的安全属性,一般置为 NULL;
dwStackSize:指定了线程的堆栈深度,一般都设置为0;
lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。一般情况为(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是线程函数名;
lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数;
dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用;
lpThreadId:该参数返回所创建线程的ID;
如果创建成功则返回线程的句柄,否则返回NULL。
2、DWORD SuspendThread(HANDLE hThread);
该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止。 3、DWORD ResumeThread(HANDLE hThread);
该函数用于结束线程的挂起状态,执行线程。 4、VOID ExitThread(DWORD dwExitCode);
该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。 5、BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);
一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某一线程的执行。各参数含义如下:
hThread:将被终结的线程的句柄;
dwExitCode:用于指定线程的退出码。
使用TerminateThread()终止某个线程的执行是不安全的,可能会引起系统不稳定;虽然该函数立即终止线程的执行,但并不释放线程所占用的资源。因此,一般不建议使用该函数。
6、BOOL PostThreadMessage(DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam);
该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。
idThread:将接收消息的线程的ID;
Msg:指定用来发送的消息;
wParam:同消息有关的字参数;
lParam:同消息有关的长参数;
调用该函数时,如果即将接收消息的线程没有创建消息循环,则该函数执行失败。

顺便说一下WaitForSingleObject函数,其函数原型为:
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);

hHandle为要监视的对象(一般为同步对象,也可以是线程)的句柄;
dwMilliseconds为hHandle对象所设置的超时值,单位为毫秒;
当在某一线程中调用该函数时,线程暂时挂起,系统监视hHandle所指向的对象的状态。如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。参数dwMilliseconds有两个具有特殊意义的值:0和INFINITE。若为0,则该函数立即返回;若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态时为止.

参考资料:http://user.qzone.qq.com/32580842

参考技术A 在MFC里直接用AfxBeginThread就可以。
使用CWinThread*声明一个线程,然后定义一个线程函数,在要开启线程的地方使用AfxBeginThread这个线程函数就可以了~
例如:
CWinThread* pThreadTest;

UINT _TestThread(LPVOID lparam)

...//sth you want to do.

return 0;


//在适当地方开启线程
pThreadTest = ::AfxBeginThread(_TestThread,this);

很简单的。
参考技术B HANDLE handle=CreateThread(null,0,fun,(LPVOID)i,0,0);
closehandle(handle);

这就是线程的创建,fun是线程函数名 i是其中的参数

以上是关于VC++多线程问题的主要内容,如果未能解决你的问题,请参考以下文章

VC++多线程问题

高手进,关于C语言在windows上建立多线程的问题(VC6.0上实现)

如何在vc++中创建多线程

孙鑫VC学习笔记:多线程编程

在VC中,多线程如何调用类得成员函数?

多线程编程