当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>C++基础入门教程

多线程编程浅析(2)——线程间通信

    上文我们介绍了如何建立一个简单的多线程程序,多线程之间不可避免的需要进行通信。相比于进程间通信来说,线程间通信无疑是相对比较简单的。

    首先我们来看看最简单的方法,那就是使用全局变量(静态变量也可以)来进行通信,由于属于同一个进程的各个线程是处于同一个进程空间中的,并且它们共享这个进程的各种资源,因此它们都可以毫无障碍的访问这个进程中的全局变量。当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前加上volatile声明,来告诉编译器这个全局变量是易变的,让编译器不要对这个变量进行优化(至于编译器到底有没有按照你的要求来对volatile进行处理这个暂且不理)。

    下面贴出一段简单的示例代码:

#include "stdafx.h"
#include 
"windows.h"
#include 
"stdio.h"

volatile int ThreadData = 0;

void ThreadProcess()
{
    
for(int i=0; i<6; i++)
    
{
        ThreadData 
+= 1000;
        Sleep(
1000);
        printf(
"Sub  Thread Tick %5d! %5d\n",(i+1)*1000, ThreadData);
    }

    printf(
"Exit Sub Thread!\n");
    
}


int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hThread;
    DWORD ThreadID;
    hThread
=CreateThread(NULL,
                     
0,
                     (LPTHREAD_START_ROUTINE)ThreadProcess,
                     NULL,
                     
0,
                     
&ThreadID);
    
    
for(int i=0; i<10; i++)
    
{
        ThreadData 
-= 600;
        Sleep(
600);
        printf(
"Main Thread Tick %5d! %5d\n", (i+1)*600, ThreadData);
    }

    printf(
"Main Thread Loop Finished! \n");
    system(
"pause");
    
return 0;
}

    除了全局变量之外,还有其他的方法,比如利用消息机制等来实现线程间通信。

    下面,关于多线程中的全局变量,我来介绍点有点偏题的东西:

    线程局部存储(TLS)

    进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG.

    如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要新的机制来实现。这就是TLS.

    线程局部存储在不同的平台有不同的实现,可移植性不太好。

    方法一:每个线程创建时系统给它分配一个LPVOID指针的数组(叫做TLS数组),这个数组从C编程角度是隐藏着的不能直接访问,需要通过一些C API函数调用访问。首先定义一些DWORD线程全局变量或函数静态变量,准备作为各个线程访问自己的TLS数组的索引变量。一个线程使用TLS时,第一步在线程内调用TlsAlloc()函数,为一个TLS数组索引变量与这个线程的TLS数组的某个槽(slot)关联起来,例如获得一个索引变量:

    global_dwTLSindex=TLSAlloc();

    注意,此步之后,当前线程实际上访问的是这个TLS数组索引变量的线程内的拷贝版本。也就说,不同线程虽然看起来用的是同名的TLS数组索引变量,但实际上各个线程得到的可能是不同DWORD值。其意义在于,每个使用TLS的线程获得了一个DWORD类型的线程局部静态变量作为TLS数组的索引变量。C/C++原本没有直接定义线程局部静态变量的机制,所以在如此大费周折。

    第二步,为当前线程动态分配一块内存区域(使用LocalAlloc()函数调用),然后把指向这块内存区域的指针放入TLS数组相应的槽中(使用TlsValue()函数调用)。

    第三步,在当前线程的任何函数内,都可以通过TLS数组的索引变量,使用TlsGetValue()函数得到上一步的那块内存区域的指针,然后就可以进行内存区域的读写操作了。这就实现了在一个线程内部这个范围处处可访问的变量。

    最后,如果不再需要上述线程局部静态变量,要动态释放掉这块内存区域(使用LocalFree()函数),然后从TLS数组中放弃对应的槽(使用TlsFree()函数)。

    方法二:

    直接声明这个变量是各个线程有自己拷贝的线程局部静态变量:

    __declspec( thread ) int var_name;

相关内容
赞助商链接