多线程编程1——基础知识

来源:互联网 发布:淘宝的渠道管理在哪里 编辑:程序博客网 时间:2024/06/05 22:50

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属于一个进程的其他的线程共享进程拥有的全部资源。

Linux进程创建一个新线程时,线程将拥有自己的栈(因为线程有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号句柄和当前目录状态。

Linux通过fork创建子进程与创建线程之间是有区别的:fork创建出该进程的一份拷贝,这个新进程拥有自己的变量和自己的PID,它的时间调度是独立的,它的执行几乎完全独立于父进程。

进程可以看成一个资源的基本单位,而线程是程序调度的基本单位,一个进程内部的线程之间共享进程获得的时间

线程相对于进程的优点:
1、和进程相比,它是一种非常”节俭”的多任务操作方式。在linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种”昂贵”的多任务工作方式。

2、运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需时间也远远小于进程间切换所需要的时间。据统计,一个进程的开销大约是一个线程开销的30倍左右。

3、线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进城下的线程之间贡献数据空间,所以一个线程的数据可以直接为其他线程所用,这不仅快捷,而且方便。

4、使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。

5、改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序才会利于理解和修改。

简单的多线程操作

1、线程创建

#include <pthread.h>int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);//thread:指向pthread_t类型的指针,用于引用新创建的线程。//attr:用于设置线程的属性,一般不需要特殊的属性,所以可以简单地设置为NULL。//*(*start_routine)(void *):传递新线程所要执行的函数地址。//arg:新线程所要执行的函数的参数。//调用如果成功,则返回值是0,如果失败则返回错误代码。

2、线程ID的获取

pthread_t pthread_self(void);//返回当前线程的ID

3、线程终止

void pthread_exit(void *retval);//retval:返回指针,指向线程向要返回的某个对象。

4、线程等待

int pthread_join(pthread_t th, void **thread_return);//th:将要等待的线程,线程通过pthread_create返回的标识符来指定。//thread_return:一个指针,指向另一个指针,而后者指向线程的返回值。不需要返回值则为NULL

5、线程分离

int pthread_detach(pthread_t thread);//成功返回0。失败返回错误值

除了局部变量以外,所有其他变量都将在一个进程中的所有线程之间共享。
线程没有像进程那样的父子关系,仅有属于同一个进程的“同组”关系(进程实际上代表的是一个线程组)。
在POSIX线程模型中,主线程可以创建一个新线程A,新线程A又可以创建另一个新线程B,线程A和B本身没有父子关系,只是同属于一个进程。
等待线程结束的pthread_join()操作可以由任何一个同组的线程发起,不必是主线程。另外,如果主线程退出,即进程退出,则所有的线程也会随之退出。

案例:
1、线程创建

void *thread_run(void* a){    while (1)    {        sleep(1);        printf ("线程 函数\n");    }}int main(){    pthread_t thread_id;    int ret = pthread_create(&thread_id, NULL, thread_run, NULL);    if (ret != 0)    {    perror ("pthread_create");    return -1;    }    while (1)    {        sleep(1);        printf ("main 函数\n");    }    return 0;}

2、参数传递

struct data{    int a;    int b;};void *thread_run(void* a){    struct data *value = (struct data*)a;     printf ("线程接收到参数: %d, %d\n", value->a, value->b);    int ret = value->a + value->b;    // 不能通过 pthread_exit 返回栈上变量的地址    pthread_exit((void*)ret);    // pthread_exit((void*)&ret);}pthread_t func(){    struct data value;    value.a = 10;    value.b = 20;    pthread_t thread_id;    // 给线程传参要注意 栈上的变量的生命周期    int ret = pthread_create(&thread_id, NULL, thread_run, (void *)&value);    if (ret != 0)    {        perror ("pthread_create");        return -1;    }    return thread_id;}int main(){    int *result;    pthread_t thread_id = func();    printf ("等待线程结束\n");    pthread_join(thread_id, (void **)&result);    printf ("result = %d\n", *result);    return 0;}