Linux基础内容(25)—— 线程控制和线程结构

Linux基础内容(24) —— 线程概念_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131294692?spm=1001.2014.3001.5501

目录

1.线程操作

1.线程创建问题

2.线程终止问题

1.exit退出

2.pthread_exit退出

 3.直接退出

3.线程等待问题

信号问题

4.线程取消

5.线程分离

2.理解线程库

重新认识线程库

1.语言层面

2.结构设计

3.pthread_id的含义


1.线程操作

1.线程创建问题

class ThreadData
{
public:pthread_t tid;char namebuffer[64];
};void *start_routine(void *args)
{ThreadData *td = static_cast(args); // 安全的强制类型转换int cnt = 10;while (cnt){cout << "new thread success, name: " << td->namebuffer << " cnt: " << cnt << endl;sleep(1);}delete td;return nullptr;
}int main()
{vector tids;
#define NUM 10for (int i = 0; i < NUM; i++){ThreadData *td = new ThreadData();snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i);pthread_create(&td->tid, nullptr, start_routine, (void *)td);tids.push_back(td);}while (true){cout << "new thread success, name: main thread" << endl;sleep(1);}return 0;
}

1.我们创造了10个线程同时进入同一个函数,该函数为可重入状态。start_routine就是可重入函数

2.此时的函数是可重入函数,因为没有出现问题。这些cnt都是临时变量,在各自的线程之中,不会互现影响,也反映了线程有自己的独立栈结构

3.循环式的创造线程不能直接循环,这样可能内部的缓冲区没有被修改,线程就创造了一堆,使得每一个线程的信息都一样

2.线程终止问题

1.exit退出

由于线程的健壮性问题,我们在任意一个执行流中调用ecit函数,都会使得整个进程退出。因为exit针对的对象为进程,我们的信号发送给进程后,自然进程结束,线程也跟着结束。

2.pthread_exit退出

 3.直接退出

如果线程执行结束,直接在函数中return。操作系统会自动回收线程

3.线程等待问题

其实线程跟进程一样都需要在结束后等待操作系统回收。如果不等待,线程对应的PCB也没有被释放,那么就会出现类似于进程的僵尸进程的内存漏泄问题。不过可以回收线程释放的信息,只释放线程。

void *start_routine(void *args)
{ThreadData *td = static_cast(args); // 安全的强制类型转换int cnt = 10;while (cnt){cout << "new thread success, name: " << td->namebuffer << " cnt: " << cnt << endl;sleep(1);}//delete td;return nullptr;
}for(auto &iter : threads)
{int n = pthread_join(iter->tid, nullptr);assert(n == 0);cout << "join : " << iter->namebuffer << " success " << endl;delete iter;
}//注意为了让已经释放空间的线程能打出其对应的名字,我们需要把start_routine中delete td进行删除
//因为我们不想要早其打印之前就被销毁,那么只能在其打印后进行释放

 对指定的thread进行等待,viod**为二级指针是线程的返回的参数。

其实线程join等待回收能将线程exit的数据进行回收

class ThreadData
{
public:int number;pthread_t tid;char namebuffer[64];
};void *start_routine(void *args)
{ThreadData *td = static_cast(args); // 安全的强制类型转换int cnt = 10;while (cnt){cout << "new thread success, name: " << td->namebuffer << " cnt: " << cnt << endl;sleep(1);// int *p = nullptr;//  p=NULL;//*p=0;}//delete td;return (void*)td->number;
}for(auto &iter : threads){void* ret=nullptr;int n = pthread_join(iter->tid, &ret);assert(n == 0);cout << "join : " << iter->namebuffer << " success, number: " <<(int_least32_t)ret<< endl;delete iter;}

 这样就能接受到信息了。当然我们也可以返回一个结构体,这样调回来的就是结构体的信息了。

信号问题

线程出问题,不会收到信号,因为如果线程挂了,进程都退出了。那么也就意味着join等待不会考虑信号的问题。

4.线程取消

线程能被其他线程取消

5.线程分离

1.线程的等待功能只有阻塞等待

2.如果线程释放时,我们需要看到线程的返回信息,那么join的函数就可以实现我们需要的

3.如果我们不想要线程的信息,也就意味着join的回收信号是无意义的

4.那么我们不能像进程那样非阻塞等待,是否可以像线程类似通过信号进行自动回收,答案是肯定的,线程分离就能将线程自动被操作系统回收

pthread_self:哪个线程调用,就得到哪个线程的pthread_t

std::string changeId(const pthread_t& pthread_id)
{char tid[128];snprintf(tid,sizeof(tid),"0x%x",pthread_id);return tid;
}void *start_routine(void *args)
{std::string threadname = static_cast(args); // 安全的强制类型转换while (true){std::cout << threadname << " running ...: " << changeId(pthread_self()) << std::endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, start_routine, (void *)"thread 1");std::string main_id=changeId(pthread_self());std::cout << "main thread running ...: " << changeId(tid) << std::endl;pthread_join(tid, nullptr);return 0;
}

两个地址完全一致,说明知道指向的线程地址是多少

pthread_detach:分离一个指定的线程

 

2.理解线程库

1.重新认识线程库

1.语言层面

mypthread:mypthread.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f mypthread
#include 
#include 
#include void thread_run()
{while (true){std::cout << "我是新线程..." << std::endl;sleep(1);}
}int main()
{std::thread t1(thread_run);while (true){std::cout << "我是主线程..." << std::endl;sleep(1);}t1.join();return 0;
}

若这样,编译器编不过,系统提示我们没有包含pthread库

1.C++11的多线程在linux的环境中其实是对pthread库的封装,也就意味着,c++创造的线程在linux下依然是轻量级进程

2.不管是什么语言,在linux的环境下都会调用pthread库。

2.结构设计

1.linux没有真正意义上的线程,只有轻量级进程。也就意味着linux提供的调用函数为轻量级进程

2.但是作为用户不会考虑linux的环境和设计,需要的是线程

3.pthread库提供的线程库就是调用linux对轻量级进程的调用函数的封装。那么也就是说其实我们使用的线程其实是库提供给我们的

4.既然库提供给我们对于的结构了,那么我们使用时一定是被管理的。那么库给我们提供的pthread就有其属性,以至于用来记录所有线程的信息

5.pthread_attr_t就是存储线程属性的结构联合体,线程的属性比进程的属性要少。

6.上面的设计其实就是用户及线程,即用户关心的线程属性在库中,内存提供了线程的函数会调用

7.用户级线程与内核轻量级进程一一对应,达到管理的作用

3.pthread_id的含义

1.在虚拟内存地址中,对应的栈就是主线程的栈

2.线程库其实就是磁盘的文件,它在被调用后会在虚拟内存的共享区中出现

3.共享区中存储着线程结构体的地址,而多个线程则是通过数组的形式排列存储在虚拟空间

4.共享区指向动态库找到映射的线程,线程中存储有局部存储和独立栈

5.那么我们就能进一步理解所谓的线程库就是封装了linux中的clone轻量级进程

6.增加__thread在内置类型全局变量前,会使得内置类型转换为线程的局部存储中,每一个线程都来一份

__thread int cnt = 0;


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部