使用POSIX Threads进行多线程编程(一)——pthread基本知识

使用POSIX Threads进行多线程编程(一)

——pthread基本知识

说明:

  1. 本文是翻译自《MultiThreaded-Programming-With-POSIX》,作者Guy Kerens。
  2. 本文预计翻译三章,主要涉及pthread基本知识互斥量(锁)条件变量,一是因为这已经能够引导读者入门,二是因为本人在工作之余翻译,实在时间捉急。
  3. 翻译:张小川,转载请保留原作者

写在开始

这份综述是为了使你熟悉使用POSIX进行多线程编程,并向你展示它的特性是如何运用到“实际”编程中的。它解释了由库定义的不同的工具,展示了如何去使用他们,并且给出了用他们解决编程问题的例子。本文假定读者对并行编程概念有一些基本的理论知识,没有这些知识背景的读者可能会觉得这些概念比较难理解。我会准备一个单独的文件来为这些只熟悉‘串行’编程的读者来解释这些理论背景知识。

我会假定熟悉异步编程模型(如使用在窗口环境中的(X, Motif))的读者,理解多线程编程的概念会更容易一些。

当说起POSIX线程时,一个不可避免的问题是“应该使用哪个版本的POSIX线程标准呢?”因为这个线程标准在几年间已经被修改过了,你会发现不同版本标准的实现有不同的函数集,不同的默认值,不同的差别。因为这个综述是在一个linux系统且内核LinuxThreads library 版本v0.5上写的,使用其他系统或者其他版本的pthread库的程序员,应该参考相应的系统的manuals以防不兼容。并且,由于一些例子使用了系统函数,他们在用户级的thread库上不能工作(参考我们的parallel programming theory tutorial以得到更多信息)。说到这,我会尝试在其他系统上检查这些例子(Solaris 2.5浮现在脑海),以让他们更加“跨平台”。


什么是线程?为什么使用线程

一个线程是“半个进程”,它有自己的栈,执行一段给定的代码。与进程不同的是,线程之间的内存通常是共享的(而不同的进程通常有不同的存储区域)。一个线程组是在同一个进程内执行的线程集合。它们共享内存,因此可以访问相同的局变量,相同的堆存储,相同的文件描述符等。所有这些线程并行执行(i.e.使用时间片,或者在多核处理器上真正达到并行)。

线程组相对于串行程序的优势是:几个操作可能并行执行,那么事件在到达时就可以被马上处理(例如,如果用一个线程处理用户接口,另一个线程处理数据库访问,那么就可以在执行用户大量查询的同时仍然响应用户的输入)。

线程组相对于进程组的优势是:线程间的上下文切换要比进程间的上下文切换快得多(上下文切换指的是系统从一个正在执行的线程或进程切换到另外一个线程或进程)。而且,线程间通信通常比进程间的通信更快且更易于实现。

另一方面,由于一个线程组使用相同的存储空间,如果一个线程导致内存的数据崩溃,其他线程也会受到影响。但是进程中,操作系统一般会保护过程彼此分离,因此如果一个进程使其存储空间崩溃,其他的进程不受影响。使用进程的另一个好处是它可以在不同的机器上跑,而线程(一般)都只在一个机器上跑。


创建和销毁线程

当一个多线程程序开始执行的时候,它有一个线程在跑,就是执行main()函数的线程。这已经是一个完整的线程,有它自己的thread ID。创建一个新的线程,应该调用pthread_create()函数。下面给出了使用它的例子:

/*
 * pthread_create.c
 */
#include
#include

void *do_loop(void *data)
{
    int i;
    int j;
    int me = *((int*)data);

    for(i = 0; i < 10; i ++)
    {
        for(j = 0; j < 5000000; j ++)
        {
            ;//just for delay
        }
        printf("'%d' - got '%d'\n", me, i );
    }

    /*terminate the thread*/
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    int thread_id;
    pthread_t p_thrd;
    int a = 1;
    int b = 2;

    thread_id = pthread_create(&p_thrd, NULL, do_loop, (void *)&a);

    //run the fun in the main thread
    do_loop((void *)&b);

    return 0;
}

关于该程序需要知道的几点:

  1. 注意main函数也是一个线程,所以它与它所创建的线程一起执行do_loop()函数;
  2. pthread_create()函数需要四个参数。第一个参数由函数 pthread_create()使用来提供该线程的信息(即线程标识符)。第二个参数用来指定新线程的属性。在我们的例子中我们传递一个NULL指针给pthread_create(),以使用默认属性。第三个参数是该线程执行程序的名称。第四个参数是传递给该函数(该线程要执行的函数)的参数。注意映射到void*并非由ANSI-C语法的要求,但是放在这儿更加清晰。
  3. 函数中的循环延迟只为了演示线程是并行执行的。如果你的CPU跑的快使用一个大的延迟;并且你会在另一个线程之前看到一个线程的所有打印。
  4. pthread_exit()函数的调用,使得线程退出并且会释放所有该线程占用的资源。在一个线程的最外层函数的结束并不需要调用这个函数,因为当其返回(return)时,该线程会自动地结束(exit)。这个函数在我们想要在一个线程中间结束它时用。

为了使用gcc来编译一个多线程程序,我们需要链接到pthread 库。假设你已经将此库安装在了你的系统,如下给出了如何编译我们的第一个程序

gcc pthread_create.c -o pthread_create -lpthread

注意在这个综述中接下来的程序中,可能会需要在该编译行中加入-D_GNU_SOURCE标志,以使得源代码能够编译。


参考:《MultiThreaded-Programming-With-POSIX》

你可能感兴趣的:(C学习,pthread,多线程)