日期:2014-05-16  浏览次数:20773 次

Linux/UNIX线程(1)

线程(1)

本文将介绍如何使用多个控制线程在单个进程环境中执行多个任务。

一个进程中的所有线程都可以访问该进程的组成部件(如文件描述符和内存)。

线程包含了表示进程内执行环境必须的信息,其中包括进程中标识线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno变量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。

线程标识

进程ID在整个系统中是唯一的。每个线程ID,线程ID只在它所属的进程环境中有效。

在线程中,线程ID的类型是pthread_t类型,由于在Linux下线程采用POSIX标准,所以,在不同的系统下,pthread_t的类型是不同的,比如在ubuntn下,是unsigned long类型,而在solaris系统中,是unsigned int类型。而在FreeBSD上才用的是结构题指针。 所以不能直接使用==判读,而应该使用pthread_equal来判断。

#include<pthread.h>

intpthread_equal(pthread_t t1, pthread_t t2);

返回值:若相等则返回非0值,否则返回0。

线程可以通过调用

#include<pthread.h>

pthread_tpthread_self(void);

返回值是调用线程的线程。

线程创建

创建线程可以调用pthread_create函数创建。

#include<pthread.h>

intpthread_create(pthread_t *thread, const pthread_attr_t *attr,

                          void*(*start_routine) (void *), void *arg);

返回值:若成功则返回0,否则返回错误编号

当pthread_create成功返回时,由thread指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性。将attr设置为NULL,创建默认属性的线程。

新创建的线程从start_routine函数地址开始运行,该函数只有一个无类型指针参数arg,如果需要向start_routine函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。

线程创建时并不能保证哪个线程会先运行:是新创建的线程还是调用线程。新创建的线程可以访问进程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但该线程的未决信号集被清除。

下面的程序创建了一个线程并且打印进程ID、新线程的线程ID以及初始化线程的线程ID。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_t ntid;

void printids(const char *s)
{
    pid_t       pid;
    pthread_t   tid;

    pid = getpid();
    tid = pthread_self();
    printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
      (unsigned int)tid, (unsigned int)tid);
}

void * thr_fn(void *arg)
{
    printids("new thread: ");
return((void *)0);
}

void err_quit(const char* fmt, ...)
{
printf("%s\n",fmt);
exit(1);
}


int main(void)
{
    int     err;

    err = pthread_create(&ntid, NULL, thr_fn, NULL);
    if (err != 0)
        err_quit("can't create thread: %s\n", strerror(err));
printids("main thread:");
    sleep(1);
    exit(0);
}

编译:

 pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,所以在使用pthread_create()创建线程,以及调用 pthread_atfork()函数建立fork处理程序时,需要链接该库。

gcc creatThread.c –lpthread

执行及输出:

./a.out

main thread: pid2846 tid 3079362240 (0xb78b56c0)

new thread:  pid 2846 tid 3079359344 (0xb78b4b70)

主线程需要休眠,否则整个进程有可能在新线程进入运行之前就终止了。新线程是通过pthread_self()来获取自己的线程ID,而不是从共享内存中读出或者从线程的启动例程中以参数接收到。pthread_create函数会通过第一个参数返回新建线程的ID。在本例中,新建线程ID被放在ntid中,但是如果新线程在主线程调用pthread_create返回之前就运行了,那么新线程看到的是未初始化的ntid内容。

线程终止

如果进程中的任一线程调用了exit,_Exit或者_exit,那么整个进程就会终止。一次类似,如果信号的默认动作是终止进程,那么,把该信号发送到线程会终止整个进程。

         但线程可以通过下列三种方式退出:

1.      线程只是从启动例程中返回,返回值是线程的退出码

2.      线程可以被同一进程中的其他线程取消

3.      线程调用pthread_exit

pthread_exit函数:

 #include<pthread.h>

voidpthread_exit(void *retval);

retval 是一个无类型的指针,与传给启动例程的单个参数类似。进程中的其他线程可以通过调用pthread_join函数访问到这个指针。

#include<pthread.h>

intpthread_join(pthread_t thread, void **retval);

返回值:若成功则返回0,否则返回错误编号

调用pthread_join函数的线程将一直被阻塞,直到指定的线程调用pthread_exit 、从启动例程中返回或者被取消。如果线程只是从它的启动例程返回,retval 将包含返回码。如果线程被取消,retval 指定的内存单元就设置为PTHREAD_CANCELED。