首页 > 代码库 > 线程的创建

线程的创建

        线程是一种使程序在同一时间做多件事的机制,和进程一样是并发执行的。linux内核调度为每个线程分配一个时间片,使用完后等待下次调度。和进程相比,线程是一种更小的执行单位。

        每个进程启动后都会有一个线程在运行,称为主线程,可以在主线程中启动多个子线程,这些线程在同一个进程中,不同线程在给定时间内执行不同的代码片段。

        我们可以fork一个子进程,这个子进程就是对父进程的一个copy,包括系统分配的各种资源:虚拟内存、文件描述符等。如果在子进程关闭文件描述符,不会影响父进程对其的读写。但线程不同,每个线程都共享同一内存、文件描述符、以及其他资源。在一个线程中关闭文件描述符,其他的线程都不能读写。

        在任何一个线程中调用exec,所有线程都会停止,并且当前线程所在的进程会被exec传递的程序替换。

1.线程创建

        所有与线程相关的函数和数据类型都在<pthread.h>中声明,但线程函数没有被包含进C标准库中,因此,调用了线程相关API后,编译时需要添加线程库:libpthread,或者-lpthread
       每个线程用一个唯一的ID表示,称为线程id,编程中类型为pthread_t.

        创建一个线程很简单,只需调用以下函数即可,创建之后线程会等待系统调用随时会启动。

 int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);
参数介绍:
 thread :线程的ID,一个pthread_t类型的指针
attr: 线程的属性,一般赋值为NULL,表示使用默认值
start_routine :线程函数,在线程创建后需要执行的代码。一个普通函数,返回值、参数类型都是void*。这样可以传递任何类型的数据,只需将数据结构的指针传递,然后接收 到时强制转换。
arg :线程函数start_routine的参数


创建两个线程,分别打印“hello”和“world”,主线程打印"main"

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

void* thread_func(void* s)
{
        int n=10;
        while(n-- >0)
        printf("%s \n",(char*)s);
        return NULL;
}

int main()
{
        int g=10;
        pthread_t thread_id_hl;
        pthread_t thread_id_wd;
        pthread_create(&thread_id_hl,NULL,thread_func,"hello");
        pthread_create(&thread_id_wd,NULL,thread_func,"world");
        while(g-- >0)
        printf("main\n");
        return 0;

}

        以上代码中创建两个线程,线程函数相同只是传递的参数不同,运行之后可以看到打印"main"个数每次都是10个,“hello”,"world"个数不够10个。只是因为main线程在两个子线程没有退出之前结束。

        要等待子线程都退出之后main线程在退出,和进程中的wait相似,线程中使用pthread_join();

int pthread_join(pthread_t thread,void **retval);
参数介绍:

thread: 等待退出线程的id
retval: 线程退出时的返回值,通过指针的方式传递个*retval,如果调用pthread_join时指定的线程已经退出,*retval 会返回PTHREAD_CANCLED
        一个线程的退出可以通过两种方式:1.等待线程函数执行到return返回 ;2.在线程中执行void pthread_exit(void *retval),retval为线程返回的值。无论用哪种返回方式,返回值都会在pthread_join的参数retval中获取。

如下程序,创建两个线程调用同一个函数,对线程函数传递的参数不同其返回值也不同:#include <pthread.h>

#include <stdlib.h>
#include <string.h>

#define THREAD_ONE      1
#define THREAD_TWO      2

void* thread_function(void *arg)
{
        char *retval;
        int n = *((int*)arg);
        switch(n)
        {
        case THREAD_ONE:
                retval="get from thread one.\n";
                return retval;
        case THREAD_TWO:
                retval="get from thread two.\n";
                pthread_exit((void*)retval);
        }      
        return NULL;

}

int main()
{
        int thread_arg1 = THREAD_ONE;
        int thread_arg2 = THREAD_TWO;
        pthread_t thread_one;
        pthread_t thread_two;
        char *retval,*mallocaddr;
        
        pthread_create(&thread_one,NULL,&thread_function,(void*)&thread_arg1);
        pthread_create(&thread_two,NULL,&thread_function,(void*)&thread_arg2);
        
        mallocaddr = (char*)malloc(64);
        memset(mallocaddr,0,64);        
        retval = mallocaddr;
        pthread_join(thread_one,(void**)&retval);
        printf("The return value. %s",retval);
        pthread_join(thread_two,(void**)&retval);
        printf("The return value. %s",retval);
        free(mallocaddr);
        return 0;
}
        如以上示例,通常一个函数可能被多个线程调用,有时候函数需要知道自己被哪个进程正在运行,可以在函数中调用:pthread_t pthread_self(void)返回正在运行函数的线程id

        如果在一个线程中调用pthread_join并将自己的id作为参数,函数会立刻出现错误,并返回EDEADLK。为了避免一个线程pthread_join自己的id,可以这样:

if (!pthread_equal (pthread_self (), other_thread))
	pthread_join (other_thread, NULL);
pthread_equal 函数定义:

int pthread_equal(pthread_t t1, pthread_t t2);
2.线程的joinable和detach状态

        joinable状态的线程,当线程结束之后不会自动释放资源,直到其他线程调用pthread_join该线程的返回值时才会释放。
        detach 状态的线程,线程结束之后会自动释放资源,其他线程不能调用pthread_join获取他的返回值。

        线程这两种状态由创建时pthread_create传递的属性attr参数来控制,默认情况下为joinable状态。

将线程设置为detach状态:

pthread_attr_t attr;
pthread_t thread;
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
pthread_create (&thread, &attr, &thread_function, NULL);
pthread_attr_destroy (&attr);
在这里,多个线程创建可以使用同一个attr,使用完后要用pthread_attr_destroy删除,下次用时在phtread_attr_init

注:一个线程创建时是joinable状态,可以用pthread_detach将其转换成detach状态,反之,不可以。