欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > Linux线程控制封装及线程互斥

Linux线程控制封装及线程互斥

2025/3/29 20:15:47 来源:https://blog.csdn.net/2302_80378107/article/details/146418223  浏览:    关键词:Linux线程控制封装及线程互斥

1.clone函数的使用

#define _GNU_SOURCE
#include <sched.h>
#include <signal.h>

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...);

fn:子进程或线程的入口函数

child_stack:子进程的栈地址,通常需要手动分配,栈的大小需要足够容纳子进程变量的局部变量和函数调用。 

  • flags:控制子进程或线程共享哪些资源(如内存、文件描述符等)。常见的标志包括:

    • CLONE_VM:共享内存空间。

    • CLONE_FS:共享文件系统信息。

    • CLONE_FILES:共享文件描述符。

    • CLONE_SIGHAND:共享信号处理机制。

    • CLONE_THREAD:让子进程成为与父进程同一线程组的线程。

    • CLONE_NEWNS:创建新的 mount namespace。

    • CLONE_NEWUTS:创建新的 UTS namespace,用于隔离主机名。

    • arg:传递给 fn 的参数。

代码示例

stack+STACK_SIZE是因为开辟的栈区使用是从高到低的,开辟栈区是从低地址到高地址,开辟后返回的地址是低地址,要用高地址使用,开辟的地址加上size就是使用的地址。

#include <sched.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>#define STACK_SIZE (1024*1024) //1MB的栈空间
//进程间通信,传递退出码
//子进程执行的函数
static int child_func(void* arg)
{while(true){printf("Child process:PID=%d\n",getpid());sleep(1);}return 0;
}
int main()
{char* stack=(char*)malloc(STACK_SIZE);//为子进程分配栈空间if(stack==NULL){perror("malloc");exit(EXIT_FAILURE);}//使用clone创建子进程pid_t pid=clone(child_func,stack+STACK_SIZE,CLONE_VM|SIGCHLD,NULL);if(pid==-1){perror("clone");free(stack);exit(EXIT_FAILURE);}printf("Parent process:PID=%d,child PID=%d\n",getpid(),pid);//等待子进程结束if(waitpid(pid,NULL,0)==-1){perror("waitpid");free(stack);exit(EXIT_FAILURE);}free(stack);return 0;
}

注意:虽然栈空间是独立的,但是线程的地址都是一起的,所以可以访问其它线程的栈空间

代码示例

可以在主线程看到创建线程的变量,另外在创建的新线程死循环会报段错误,因为线程的调度和切换,操作系统可能会回收栈空间,这样就会把a回收,主线程就无权限访问了。

#include <sched.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>int* p=nullptr;void* threadrun(void* args)
{int a=123;p=&a;//while(true) {sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,threadrun,nullptr);while(true){std::cout<<"*p:"<<*p<<std::endl;sleep(1);}pthread_join(tid,nullptr);return 0;
}

代码示例二

Thread.hpp文件

#ifndef _THREAD_H_
#define _THREAD_H_#include <iostream>
#include <string>
#include <pthread.h>
#include <cstdio>
#include <cstring>
#include <functional>namespace ThreadMoudle
{static int number = 1; // bugclass Thread{//把MAIN的方法进行包装using func_t =std::function<void()>;private:void EnableDetach(){std::cout<<"线程被分离"<<std::endl;_isdetach=true;}void EnableRunning(){_isrunning=true;}//传的是this指针,所以是thread*类型static void* Routine(void* args)//属于类内的成员函数,默认包含this指针,会多一个参数导致参数不匹配{//这里会把私有成员进行改变,因为执行流已经开始了,分离状态和运行状态此刻要确定好Thread* self=static_cast<Thread*>(args);self->EnableRunning();if(self->_isdetach)self->Detach();pthread_setname_np(self->_tid,self->_name.c_str());self->_func();return nullptr;}public://要接受执行什么任务Thread(func_t func):_tid(0),_isdetach(false),_isrunning(false),_res(nullptr),_func(func){_name="thread-"+std::to_string(number++);}void Detach(){if(_isdetach)return;if(_isrunning)pthread_detach(_tid);EnableDetach();}bool Start(){if(_isrunning)return false;int n=pthread_create(&_tid,nullptr,Routine,this);if(n!=0){std::cerr<<"create pthread error"<<strerror(n)<<std::endl;return false;}else{std::cout<<_name<<"create success"<<std::endl;return true;}}bool Stop(){if(_isrunning){int n=pthread_cancel(_tid);if(n!=0){std::cerr<<"cancel thread error"<<strerror(n)<<std::endl;return false;}else{_isrunning =false;std::cout<<_name<<"stop"<<std::endl;return true;}}return false;}void Join(){if(_isdetach){std::cout<<"你的线程已经是分离的,不能进行join"<<std::endl;return;}int n=pthread_join(_tid,&_res);if(n!=0){std::cerr<<"fail"<<strerror(n)<<std::endl;}else{std::cout<<"join success"<<std::endl;}}~Thread(){}private:pthread_t _tid;std::string _name;bool _isdetach;bool _isrunning;func_t _func;void* _res;};
}
#endif

MAIN.cc文件

#include "Thread.hpp"
#include <unistd.h>
#include <vector>using namespace ThreadMoudle;int main()
{// std::vector<Thread> threads;// for(int i=0;i<10;i++)// {//     threads.emplace_back([](){//         while(true)//         {//             char name[128];//             pthread_getname_np(pthread_self(),name,sizeof(name));//             std::cout<<"我是一个新线程"<<name<<std::endl;//线程名字//             sleep(1);//         }//     });// }// for(auto& thread:threads)// {//     thread.Start();// }// for(auto& thread:threads)// {//     thread.Join();// }//传递执行的任务Thread t([](){while(true){char name[128];pthread_getname_np(pthread_self(),name,sizeof(name));std::cout<<"我是一个新线程:"<<name<<std::endl;sleep(1);}});t.Start();//t.Detach();sleep(5);t.Stop();sleep(5);t.Join();return 0;
}

pthread_setname_np函数

#define _GNU_SOURCE
#include <pthread.h>int pthread_setname_np(pthread_t thread, const char *name);

 

  • thread:要设置名称的线程的 ID。

  • name:要设置的线程名称,长度限制为 16 个字符(包括终止符 \0)。

返回值

  • 成功时返回 0。

  • 出错时返回非零错误码。

错误码

  • ERANGEname 的长度超过了允许的限制。

pthread_getname_np函数

#define _GNU_SOURCE
#include <pthread.h>int pthread_getname_np(pthread_t thread, char *name, size_t len);

 

参数

  • thread:要获取名称的线程的 ID。

  • name:一个字符数组,用于存储获取到的线程名称。

  • lenname 数组的长度。

返回值

  • 成功时返回 0。

  • 出错时返回非零错误码:

    • EINVALlen 小于 1 或大于 NAME_MAX(通常是 16)。

    • ESRCH:指定的线程不存在。

你提到的问题非常关键,这是 C++ 中多线程编程时常见的一个陷阱。在 C++ 中,类的成员函数默认有一个隐含的 `this` 指针参数,这会导致成员函数的签名与 `pthread_create` 或其他线程库要求的回调函数签名不匹配。


你正确地使用了 `static` 关键字来定义 `Routine` 函数。这是因为:
1. **非静态成员函数**:非静态成员函数会隐含一个 `this` 指针作为第一个参数,这会导致函数签名与 `pthread_create` 需要的回调函数签名不匹配。
2. **静态成员函数**:静态成员函数没有 `this` 指针,因此可以作为线程回调函数。

### 关键点解释
1. **静态成员函数作为回调**:
   - `Routine` 是一个静态成员函数,没有隐含的 `this` 指针。
   - 它的签名与 `pthread_create` 需要的回调函数签名完全匹配:`void* (*)(void*)`。

2. **传递 `this` 指针**:
   - 在调用 `pthread_create` 时,将 `this` 指针作为参数传递给 `Routine`。
   - 在 `Routine` 中,使用 `static_cast<Thread*>(args)` 将 `void*` 参数转换回 `Thread*` 类型。

3. **线程生命周期管理**:
   - 在 `Thread` 的构造函数中调用 `pthread_create` 创建线程。
   - 在 `Thread` 的析构函数中调用 `pthread_join` 等待线程结束,确保线程安全退出。

### 总结
你提到的问题非常关键,C++ 的成员函数由于隐含的 `this` 指针,不能直接用作线程回调函数。通过将回调函数定义为静态成员函数,并将 `this` 指针作为参数传递,可以解决这个问题。希望这个解决方案对你有帮助!

多线程创建执行任务

 把方法放到vector里面存储,然后范围foe逐一执行任务。

#include "thread.hpp"
#include <unistd.h>
#include <vector>
#include <iterator>  // 或 <algorithm>
#include<algorithm>
using namespace ThreadMoudle;int main()
{std::vector<Thread> threads;for (int i = 0; i < 10; i++){threads.emplace_back([](){while(true){char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));std::cout << "我是一个新线程: " << name << std::endl; // 我的线程的名字是什么呀?debugsleep(1);} });}for (auto &thread : threads){thread.Start();}for (auto &thread : threads){thread.Join();}// Thread t([](){//     while(true)//     {//         char name[128];//         pthread_getname_np(pthread_self(), name, sizeof(name));//         std::cout << "我是一个新线程: " << name << std::endl; // 我的线程的名字是什么呀?debug//         sleep(1);//     }// });// t.Start();// t.Detach();// sleep(5);// t.Stop();// sleep(5);// t.Join();return 0;
}

模板参数形式

Thread.cpp文件

#ifndef _THREAD_H_
#define _THREAD_H_#include <iostream>
#include <string>
#include <pthread.h>
#include <cstdio>
#include <cstring>
#include <functional>namespace ThreadModlue
{static uint32_t number = 1; // bugtemplate<typename T>class Thread{using func_t = std::function<void(T)>; // 暂时这样写,完全够了private:void EnableDetach(){std::cout << "线程被分离了" << std::endl;_isdetach = true;}void EnableRunning(){_isrunning = true;}static void *Routine(void *args) // 属于类内的成员函数,默认包含this指针!{Thread<T> *self = static_cast<Thread<T> *>(args);self->EnableRunning();if (self->_isdetach)self->Detach();self->_func(self->_data); // 回调处理return nullptr;}// bugpublic:Thread(func_t func, T data): _tid(0),_isdetach(false),_isrunning(false),res(nullptr),_func(func),_data(data){_name = "thread-" + std::to_string(number++);}void Detach(){if (_isdetach)return;if (_isrunning)pthread_detach(_tid);EnableDetach();}bool Start(){if (_isrunning)return false;int n = pthread_create(&_tid, nullptr, Routine, this);if (n != 0){std::cerr << "create thread error: " << strerror(n) << std::endl;return false;}else{std::cout << _name << " create success" << std::endl;return true;}}bool Stop(){if (_isrunning){int n = pthread_cancel(_tid);if (n != 0){std::cerr << "create thread error: " << strerror(n) << std::endl;return false;}else{_isrunning = false;std::cout << _name << " stop" << std::endl;return true;}}return false;}void Join(){if (_isdetach){std::cout << "你的线程已经是分离的了,不能进行join" << std::endl;return;}int n = pthread_join(_tid, &res);if (n != 0){std::cerr << "create thread error: " << strerror(n) << std::endl;}else{std::cout << "join success" << std::endl;}}~Thread(){}private:pthread_t _tid;std::string _name;bool _isdetach;bool _isrunning;void *res;func_t _func;T _data;};
}#endif

Main.cc文件

可以传递多类型参数执行任务,传递对象,可以设置多个成员,这样执行完后,就可以把得到执行完后的成员变量的值。

#include "Thread.hpp"
#include <unistd.h>using namespace ThreadModlue;// 我们可以传递对象吗???
// class ThreadData
// {
// public:
//     pthread_t tid;
//     std::string name;
// };// void Count(ThreadData td)
// {
//     while (true)
//     {
//         std::cout << "我是一个新线程" << std::endl;
//         sleep(1);
//     }
// }int main()
{// ThreadData td;// Thread<ThreadData> t(Count, td);// t.Start();// t.Join();// Thread t([](){//     while(true)//     {//         std::cout << "我是一个新线程" << std::endl;//         sleep(1);//     }// });// t.Start();// t.Detach();// sleep(5);// t.Stop();// sleep(5);// t.Join();return 0;
}

线程局部存储

两个线程,一个打印并改变变量,一个只打印,可以看到变量是一起改变的,所以要想这个变量不被其它线程看到,就需要加__thread前缀,线程局部存储也有限制,只能存储内置类型和部分指针。

代码示例

#include <pthread.h>
#include <iostream>
#include <string>
#include <unistd.h>// 该count叫做线程的局部存储!
__thread int count = 1;
// 线程局部存储有什么用?全局变量,我又不想让这个全局变量被其他线程看到!
// 线程局部存储,只能存储内置类型和部分指针std::string Addr(int &c)
{char addr[64];snprintf(addr, sizeof(addr), "%p", &c);return addr;
}void *routine1(void *args)
{(void)args;while (true){std::cout << "thread - 1, count = " << count << "[我来修改count], "<< "&count: " << Addr(count) << std::endl;count++;sleep(1);}
}void *routine2(void *args)
{(void)args;while (true){std::cout << "thread - 2, count = " << count<< ", &count: " << Addr(count) << std::endl;sleep(1);}
}int main()
{pthread_t tid1, tid2;pthread_create(&tid1, nullptr, routine1, nullptr);pthread_create(&tid2, nullptr, routine2, nullptr);pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);return 0;
}

2.线程互斥

进程线程间的互斥相关背景概念

临界资源:多线程执行流共享的资源就叫临界资源

临界区:每个线程内部,访问临界资源的代码,就叫做临界区

互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用

原子性:不会被任何调度机制打断的操作,该操作只用两种状态,要么完成,要么未完成

互斥量mutex

互斥量是一种同步原语,用于保护共享资源,防止多个线程同时访问。在多线程环境中,互斥量可以确保同一时间只有一个线程可以访问特定的资源,从而避免数据竞争和未定义行为。

代码示例

模拟抢票,因为有了锁,所以不会出现票为负数的情况

// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>int ticket = 1000;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *route(void *arg)
{char *id = (char *)arg;while (1){pthread_mutex_lock(&lock);if (ticket > 0) // 1. 判断{usleep(1000);                               // 模拟抢票花的时间printf("%s sells ticket:%d\n", id, ticket); // 2. 抢到了票ticket--;                                   // 3. 票数--pthread_mutex_unlock(&lock);}else{pthread_mutex_unlock(&lock);break;}}return nullptr;
}int main(void)
{pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, route, (void *)"thread 1");pthread_create(&t2, NULL, route, (void *)"thread 2");pthread_create(&t3, NULL, route, (void *)"thread 3");pthread_create(&t4, NULL, route, (void *)"thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);
}

初始化互斥量:

pthread_mutex=PTHREAD_NUTEX_INITIALIZER

互斥量加锁和解锁

int pthread_mutex_lock(pthread_mutex_t* mutex);

int pthread_mutex_unlock(pthread_mutex_t* mutex); 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词