Linux 多线程编程基础

Linux多线程编程基础

进程与线程的区别

  1. 进程的内存是独立的,难以共享,不同进程间只能通过管道、消息、信号量、共享内存等方式进行通信。而一个进程可以包含多个线程,线程间是共享一个进程中的全局内存的。
  2. 进程的创建效率和开销都比线程大,创建线程比创建进程快10倍,甚至更多。

注意:编译linux线程一定要增加 -lpthread 编译选项。

创建线程

创建线程的函数定义:

#include <pthread.h>
/*
 *  @Description: 创建一个新线程
 *  @Para       : pthread_t * thread            保存新创建的线程地址
 *                const pthread_attr_t * attr   线程属性,默认属性可以输入NULL
 *                void *(*start)(void *)        线程主体函数
 *                void *arg                     传递给线程的参数
 * 
 *  @return     : 成功返回0,失败返回正数错误码
**/
int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start)(void *), void *arg);

例:创建两个打印线程

#include <unistd.h>
#include <pthread.h>
#include <iostream>

using namespace std;

void * pthread_one(void * arg);
void * pthread_two(void * arg);

int main(void)
{
    pthread_t thread_ID[2];
    int print_times[2] = {4,5};
    if(pthread_create(&thread_ID[0], NULL, pthread_one, &print_times[0]) != 0)
    {
        cout << "pthread one create failed!\r\n" << endl;
        return 0;
    }
    if(pthread_create(&thread_ID[1], NULL, pthread_two, &print_times[1]) != 0)
    {
        cout << "pthread two create failed!\r\n" << endl;
        return 0;
    }

    while (1)
    {
        /* code */
    }
    return 0;
}

void * pthread_one(void * arg)
{
    int times = *(int *)arg;    //操作输入数据指针时一定要注意数据的时效,确保输入的数据有效。
    pthread_detach(pthread_self());
    for(int i = 0; i < times; i++)
    {
        cout << "pthread_one" << endl;
        sleep(2);
    }
    return NULL;
}

void * pthread_two(void * arg)
{
    int times = *(int *)arg;
    pthread_detach(pthread_self());
    for(int i = 0; i < times; i++)
    {
        cout << "pthread_two" << endl;
        sleep(2);
    }
    return NULL;
}
  1. pthread_t * thread 可以保存新创建的函数线程号,后续删除线程、查看线程属性等相关操作均需要;
  2. const pthread_attr_t * attr 为线程属性结构体,POSIX标准中,通过线程属性可以设置线程的优先级、分离状态、堆栈大小等信息。但是目前不同操作系统对该结构的实现机制不同,可能无法实现相应功能;
  3. void *(*start)(void *) 线程主体函数类型,start为线程函数的指针,该线程服务函数一定要包含 void * 参数并返回 void * ;
  4. void * arg 为传入到线程主体函数的指针,即创建线程时实际运行了 (*start)(arg) 这样的一个函数,arg指针可以指向一个结构或者一个数据。
  5. 线程的返回值可以通过pthread_join()获取,如果线程在运行过程中被取消,会返回PTHREAD_CANCELED。

终止线程

如下方式可以终止线程运行:

  1. 线程函数主体 void ( start)(void *) 通过return返回制定值;
  2. 调用pthrea_exit()退出线程;
    注意:使用pthread_exit()返回retval指针,不应该为线程栈(局部变量、全局变量)中的值,因为线程exit后其资源可能被回收,retval指针指向的内容是不确定的。
#include <pthread.h>
/*
 *  @Description: 退出线程
 *  @Para       : void * retval    退出线程时的返回值,可以通过pthread_join()函数获取该范围值;
**/
void pthread_exit(void * retval);
  1. 调用pthread_cancel()取消线程;
    注意:该函数仅发送一个取消线程的请求,然后会立即返回,但是线程后续是否被取消,由线程属性决定
#include <pthread.h>
/*
 *  @Description: 取消一个线程
 *  @Para       : pthread_t thread    要取消的线程ID
 *  @return     : 成功返回0,失败返回正数错误码
**/
int pthread_cancel(pthread_t thread);
  1. 调用pthread_kill()向线程发送信号
    该应用与进程的kill()应用类型,通过该函数可以向一个已知ID的线程中发送一个信号,如SIGQUIT,SIGKILL,SIGALRM等。
    注意:使用pthread_kill时,接收信号的线程必须先用sigaction函数注册该信号的处理函数,否则该信号会被发送到程序的主线程,有可能会造成整个程序的segment fault。
    原文解释:http://man7.org/linux/man-pages/man3/pthread_kill.3.html
    Signal dispositions are process-wide: if a signal handler isinstalled, the handler will be invoked in the thread thread, but if the disposition of the signal is “stop”, “continue”, or “terminate”, this action will affect the whole process.
#include <signal.h>
/*
 *  @Description: 向线程发送一个信号
 *  @Para       : pthread_t thread    要操作的线程ID
 *  @return     : 成功返回0,失败返回错误码
**/
int pthread_kill(pthread_t thread, int sig);
  1. 进程终止后,进程内容的所有线程都会终止。

线程的其他操作

  1. 获取当前线程自身的ID
    pthread_t pthread_self(void);
    注意:在Linux的线程实现中,线程ID在所有进程中都是唯一的。但是其它类型系统未必如此,应用程序如果使用线程ID来标识其它进程的线程,其可移植性将无法保证。

  2. 判断两个线程ID是否相同
    int pthread_equal(pthread_t t1, pthread_t t2);
    注意:线程ID为pthread_t类型,linux中将其定义为一个 unsinged long ,但其它类型系统可能定义的是一个指针或者结构,用户应该将pthread_t类型作为一个不透明的类型,使用该函数进行ID判断,而不能使用"=="判断线程ID是否相等。

  3. 使用pthread_join连接两个已经终止的线程
    使用pthread_join可以等待由其参数标定的线程终止,并且阻塞调用pthread_join的线程。该调用可获取线程的返回值,并且回收终止线程的资源,若线程未能在线程终止时回收资源,则终止的线程会变为僵尸线程,僵尸线程累积过多将无法创建新的线程。
    使用pthread_join()的主要目的有两个:

  • 获取线程的返回值;
  • 释放线程的资源;
#include <pthread.h>
/*
 *  @Description: 等待一个线程的结束,并且接收结束线程的返回值,并且回收结束线程的资源
 *  @Para       : pthread_t thread      要操作的线程ID
                  void ** retval        存储线程返回值(指针类型)的指针
 *  @return     : 成功返回0,失败返回正数错误码
**/
int pthread_join(pthread_t thread, void **retval);
  1. 使用线程分离pthread_detach实现终止线程的资源自动回收
    实际使用的大部分情况,用户不需要获取线程的返回值,也不想知道线程什么时候终止,但线程终止时又必须回收线程资源,这就是pthread_detach的作用。调用pthread_detach分离的线程可以在线程终止的时候自动回收线程资源。
    一旦线程配置为分离状态,就不能再对该线程使用pthread_join,并且线程的分离配置是不可逆的。
#include <pthread.h>
/*
 *  @Description: 分离线程,线程结束时自动回收线程资源
 *  @Para       : pthread_t thread      要操作的线程ID
 *  @return     : 成功返回0,失败返回正数错误码
**/
int pthread_detach(pthread_t thread);

//例:在创建线程的线程主体函数一开始调用如下语句,可实现该线程的自动分离配置。线程结束时自动回收资源
pthread_detach(pthread_self());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值