七、Linux系统编程:多线程编程

7 多线程编程

7.1 概念

线程是比进程更小的能独立运行的基本单位,线程基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如线程ID,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

一个线程包含以下内容:
1、指向当前被执行指令的指令指针;
2、栈;
3、寄存器值的集合,定义了一部分描述正在执行线程的处理器状态的值;
4、私有的数据区

在这里插入图片描述
查看线程:man 7 threads

7.2 查看线程

  • 命令
命令含义
ps -T -p <pid>-T开启线程查看
top -H -p <pid>-H开启线程查看
  • 文件
文件含义
/proc/{PID}/task/线程默认的名字和进程名相同
/proc/{PID}/task/{tid}/comm线程名

7.3 操作

操作函数
线程标识pthread_t pthread_self(void)
线程创建int pthread_create(pthread_t * tidp, pthread_attr_t * attr, void *(*start_rtn)(void), void * arg)
子线程终止void pthread_exit(void* retval)
线程合并int pthread_join(pthread_t tid, void **retval)
线程分离int pthread_detach(pthread_t tid)
发送信号int pthread_kill(pthread_t tid, int sig)

7.3.1 线程标识

pthread_t pthread_self(void)
  • 返回值
    当前线程的线程ID

注:
1、线程ID打印使用%lu
2、pthread_self()不链接库pthread返回值为0.

linux上的线程实现就是在内核支持的基础上以POSIX thread的方式对外封装了接口。

7.3.2 线程创建

int pthread_create(pthread_t * tidp, pthread_attr_t * attr, void *(*start_rtn)(void), void * arg)
  • 参数
参数含义
tidp线程ID指针
attr线程属性

7.3.3 子线程退出

void pthread_exit(void* retval)
  • 参数
    retval :函数的返回指针,只要pthread_join中的第二个参数retval不是NULL,这个值将被传递给retval

用在线程回调函数中,返回线程数据

  • 子线程退出有两种方式
    1、线程处理函数return
    2、调用子线程终止

7.3.4 线程合并

int pthread_join(pthread_t tid, void **retval)
  • 参数
参数含义
tid被等待的线程标识符
retval一个用户定义的指针,它可以用来存储被等待线程的返回值
  • 返回值
返回值含义
0成功
非0错误码

注:可以由其他线程终止,回收资源

7.3.5 线程分离

int pthread_detach(pthread_t tid)
  • 参数
    tid:要释放线程的标识符ID

  • 返回值

返回值含义
0成功
非0错误码

注:不能被其他线程终止,存储资源在它终止时由系统自动回收释放
线程分离后不能使用join

7.4 进程线程比较

7.4.1 接口对比

进程线程描述
getpid()pthread_self()获得控制流的id
fork()pthread_create()创建新的控制流
exit()pthread_exit()退出已有的控制流
waitpid()pthread_join()等待控制流并获得结束代码

7.4.2 特性对比

特性进程线程
粒度系统资源分配和调度的基本单位CPU调度和分派的基本单位
资源有独立的地址空间共享进程的地址空间
效率上下文切换要较慢上下文切换较快
稳定性子进程崩溃,不影响父进程与其他子进程任何一个线程崩溃,整个程序崩溃

7.4.3 进程与线程的区别

  • 概念
    进程: 进程是一个具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统资源分配和独立运行的最小单位;
    线程: 线程是进程的一个执行单元,是任务调度和系统执行的最小单位;
  • 区别
    1)进程是资源(包括内存、打开的文件等)分配的单位,线程是 CPU 调度的单位;
    2)进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈;
    3)线程同样具有就绪、阻塞、执行三种基本状态,同样具有状态之间的转换关系;
    4)线程能减少并发执行的时间和空间开销;

C++中采用多线程实现并发。

7.4.4 如何选择进程或线程

(1)需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的。
(2)线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程序的响应。
(3)因为对CPU系统的效率使用上线程更占优,所以可能要发展到多机分布的用进程,多核分布用线程;
(4)并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求;
(5)需要更稳定安全时,适合选择进程;需要速度时,选择线程更好。
如何创建进程:fork()exec()、系统函数system()

7.5 C++11 thead类

实例:使用pthread实现简单的thread类。

#ifndef __THREAD_H
#define __THREAD_H
#include <pthread.h>

namespace miniSTL{
    class thread{
        typedef pthread_t id;
        typedef void* (*func_t)(void*);
        typedef void (*funcv_t)();
        id _id; // 线程TID
    public:
         thread():_id(0){}
         thread(func_t func){
            pthread_create(&_id,NULL,func,NULL);
         }
         thread(funcv_t func){
            pthread_create(&_id,NULL,reinterpret_cast<func_t>(func),NULL);
         }
         id get_id()const{
            return _id;
         }
         void join(){
            pthread_join(_id,NULL);
         }
         void detach(){
            pthread_detach(_id);
         }
    };

};
#endif // __THREAD_H

7.6 实例

7.6.1 实例1:多线程操作

#include <pthread.h>
#include <unistd.h>
#include <iostream>
//线程并发执行
using namespace std;
void* handle(void* p){
    int* pn = (int*)p;
    for(int i = 0;i < 5;++i){
        sleep(1);
        cout << pthread_self() << ":" << --*pn << endl;
    }
    return NULL;
}
int main(){ //主线程
    cout << getpid() << endl;
    cout << pthread_self() << endl;

    int n = 0;
    pthread_t tid;
    pthread_create(&tid,NULL,handle,&n); //创建一个子线程
    for(int i = 0;i < 5;++i){
        sleep(1);
        cout << pthread_self() << ":" << ++n << endl;
    }
}
[root@localhost 6]# ./a.out 
4112
139760152852288
139760152852288:1
139760134895360:0
139760152852288:1
139760134895360:0
139760152852288:1
139760134895360:0
139760152852288:1
139760134895360:0
139760152852288:1

可以看出,主线程和子线程并发进行。

但是若主线程提前退出,则子线程也退出,例如:

#include <pthread.h>
#include <unistd.h>
#include <iostream>
//线程并发执行
using namespace std;
void* handle(void* p){
    int* pn = (int*)p;
    for(int i = 0;i < 10;++i){
        sleep(1);
        cout << pthread_self() << ":" << --*pn << endl;
    }
    return NULL;
}
int main(){
    //每个进程里默认有一个线程  
    //主线程
    cout << getpid() << endl;
    cout << pthread_self() << endl;

    int n = 0;
    pthread_t tid;
    pthread_create(&tid,NULL,handle,&n); //创建一个子线程
    for(int i = 0;i < 5;++i){
        sleep(1);
        cout << pthread_self() << ":" << ++n << endl;
    }    
}
4357
139760237700928
139760237700928:1
139760219744000:0
139760237700928:1
139760219744000:0
139760237700928:1
139760219744000:0
139760237700928:1
139760219744000:0
139760237700928:1

可以通过线程合并pthread_join()解决

#include <pthread.h>
#include <unistd.h>
#include <iostream>
//线程并发执行
using namespace std;
void* handle(void* p){
    int* pn = (int*)p;
    for(int i = 0;i < 10;++i){
        sleep(1);
        cout << pthread_self() << ":" << --*pn << endl;
    }
    return NULL;
}
int main(){
    //每个进程里默认有一个线程  
    //主线程
    cout << getpid() << endl;
    cout << pthread_self() << endl;

    int n = 0;
    pthread_t tid;
    pthread_create(&tid,NULL,handle,&n); //创建一个子线程
    for(int i = 0;i < 5;++i){
        sleep(1);
        cout << pthread_self() << ":" << ++n << endl;
    }
    pthread_join(tid,NULL);
}
4428
140245492279104
140245492279104:1
140245474322176:0
140245492279104:1
140245474322176:0
140245492279104:1
140245474322176:0
140245492279104:1
140245474322176:0
140245492279104:1
140245474322176:0
140245474322176:-1
140245474322176:-2
140245474322176:-3
140245474322176:-4
140245474322176:-5

7.6.2 实例2:为多线程加入信号量

在多线程编程中,为防止数据竞争,通常需要加入多线程同步,主要方式有信号量、互斥量、条件变量和读写锁四种方式。

实例:为多线程加入信号量

#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <semaphore.h>
//线程并发
sem_t sem;
using namespace std;
void* handle(void* p) {
    int* pn = (int*)p;
    for(int i = 0; i < 10; i++) {
        sleep(1);
	sem_wait(&sem);
        cout << pthread_self() << ":" << --*pn << endl;
        sem_post(&sem);
    }
    delete pn;
    return NULL;
}
pair<pthread_t,int*> test() {
    // int* p = new int(0); //p存放默认0
    int* p = new int; //p存放默认0
    pthread_t tid;
    pthread_create(&tid,NULL,handle,p);
    //return pair<pthread_t,int*>(tid,p);
    return {tid,p};
}
int main() { // 主线程
    sem_init(&sem,0,1);
    cout << getpid() << endl;
    cout << pthread_self() << endl;
    /*C++11
    auto pi = test();
    tid = pi.first;
    int* p = pi.second;
    */
    auto [tid,p] = test();//C++17
    for(int i = 0; i < 5; i++) {
        sleep(1);
	sem_wait(&sem);
        cout << pthread_self() << ":" << ++*p << endl;
	sem_post(&sem);
    }
    pthread_join(tid,NULL);
    sem_destroy(&sem);
    return 0;
}
[root@localhost 6]# ./a.out 
5431
140018458928960
140018458928960:1
140018440972032:0
140018458928960:1
140018440972032:0
140018458928960:1
140018440972032:0
140018458928960:1
140018440972032:0
140018458928960:1
140018440972032:0
140018440972032:-1
140018440972032:-2
140018440972032:-3
140018440972032:-4
140018440972032:-5
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值