赞
踩
本文作为我对于线程的简单总结,线程控制的知识总结
创建线程并不是越多越好,进程的执行效率,一定随着线程的数量增多,效率为正态分布的(线程的切换)。线程的个数最好 = CPU的个数 * CPU的核数
创建5个线程,其中线程thread-4触发除0异常。
#include <iostream> #include <pthread.h> #include <unistd.h> #include <string> #include <functional> #include <time.h> #include <vector> using func_t = std::function<void()>; const int threadnum = 5; class ThreadDate { public: ThreadDate(const std::string &name, const uint64_t &time, func_t f) : threadname(name), createtime(time), func(f) { } public: std::string threadname; uint64_t createtime; func_t func; }; void Print() { std::cout << "执行的任务..." << std::endl; } void *threadRoutine(void *args) { ThreadDate *td = static_cast<ThreadDate *>(args); while (true) { std::cout << "new thread " << " name: " << td->threadname << " create time: " << td->createtime << std::endl; td->func(); if(td->threadname == "thread-4") { std::cout << td->threadname << "触发异常" << std::endl; int a = 10; a /= 0; } sleep(1); } } int main() { std::vector<pthread_t> pthreads; for (int i = 0; i < threadnum; ++i) { char threadname[64]; snprintf(threadname, sizeof(threadname), "%s-%d", "thread", i); pthread_t tid; ThreadDate *td = new ThreadDate(threadname, (uint64_t)time(nullptr), Print); pthread_create(&tid, nullptr, threadRoutine, (void *)td); pthreads.push_back(tid); sleep(1); } while (true) { std::cout << "main thread" << std::endl; sleep(1); } return 0; }
其中thread-4线程触发异常,进程直接终止。
进程和线程的关系如下:
函数原型: int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
功能:创建一个新的线程
参数:thread:返回线程ID
attr:设置线程属性,attr为nullptr表示使用默认属性
start_routine:是函数指针,为线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0,失败返回错误码
错误检查:传统的一些函数,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。pthreads函数出错时不会设置全局变量errno,而是将错误码通过返回值返回。pthreads也同样提供线程内errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值的判定,因为读取返回值要比读取线程内的errno变量更方便。
下面我们要创建一个新线程循环打印"new thread’,主线程循环打印"main thread"
#include <iostream> #include <pthread.h> #include <unistd.h> void *threadRoutine(void *args) { while(true) { std::cout << "new thread" << std::endl; sleep(1); } } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, nullptr); while(true) { std::cout << "main thread" << std::endl; sleep(1); } return 0; }
编译时会有报错,编译器不认识pthread_create这个函数,但我们不是加了头文件吗?
我们知道linux没有真正的线程,只有轻量级进程的概念,所以操作系统只会提供轻量级进程创建的系统调用,不会直接提供线程创建的接口。linux为了解决这一问题,就在软件层创建了pthread原生线程库对用户提供线程控制接口,在内部将线程与轻量级进程一一对应。
这样有什么好处?标准化和兼容性(pthread编写的多线程应用程序在遵循POSIX标准的各种Unix-like系统上(包括Linux)都具有很高的可移植性),灵活性:pthread库提供了一套丰富的API,用于线程的创建、管理、同步和通信。这使得开发者可以根据应用程序的需求灵活地创建和管理线程,实现复杂的并发和并行计算任务。
结果如上。
如何给创建的线程传参?
如上,arg的类型是void*类型,表示任意类型的指针。也就是说,我们可以穿一个整形,字符串,结构体对象。如下传递结构体。
#include <iostream> #include <pthread.h> #include <unistd.h> #include <string> #include <functional> #include <time.h> using func_t = std::function<void()>; class ThreadDate { public: ThreadDate(const std::string &name, const uint64_t &time, func_t f) :threadname(name), createtime(time), func(f) {} public: std::string threadname; uint64_t createtime; func_t func; }; void Print() { std::cout << "执行的任务..." << std::endl; } void *threadRoutine(void *args) { ThreadDate * td = static_cast<ThreadDate*>(args); while(true) { std::cout << "new thread "<< " name: " << td->threadname << " create time: " << td->createtime<< std::endl; td->func(); sleep(1); } } int main() { pthread_t tid; ThreadDate * td = new ThreadDate("thread-1", (uint64_t)time(nullptr), Print); pthread_create(&tid, nullptr, threadRoutine, (void *)td); while(true) { std::cout << "main thread" << std::endl; sleep(1); } return 0; }
那如何一次创建多个线程?与创建多个子进程类似。
#include <iostream> #include <pthread.h> #include <unistd.h> #include <string> #include <functional> #include <time.h> #include <vector> using func_t = std::function<void()>; const int threadnum = 5; class ThreadDate { public: ThreadDate(const std::string &name, const uint64_t &time, func_t f) : threadname(name), createtime(time), func(f) { } public: std::string threadname; uint64_t createtime; func_t func; }; void Print() { std::cout << "执行的任务..." << std::endl; } void *threadRoutine(void *args) { ThreadDate *td = static_cast<ThreadDate *>(args); while (true) { std::cout << "new thread " << " name: " << td->threadname << " create time: " << td->createtime << std::endl; td->func(); sleep(1); } } int main() { std::vector<pthread_t> pthreads; for (int i = 0; i < threadnum; ++i) { char threadname[64]; snprintf(threadname, sizeof(threadname), "%s-%d", "thread", i); pthread_t tid; ThreadDate *td = new ThreadDate(threadname, (uint64_t)time(nullptr), Print); pthread_create(&tid, nullptr, threadRoutine, (void *)td); pthreads.push_back(tid); sleep(1); } while (true) { std::cout << "main thread" << std::endl; sleep(1); } return 0; }
下面代码,创建线程并通过返回值打印其线程id。
#include <iostream> #include <pthread.h> #include <string> #include <unistd.h> void *threadRoutine(void *args) { std::string name = static_cast<const char*>(args); while(true) { std::cout << "new thread, name: " << name << std::endl; sleep(1); } } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, (void*)"thread-1"); while(true) { std::cout << "main thread, sub thread: " << tid << std::endl; sleep(1); } return 0; }
很明显,创建出来的线程id与LWP并不同,那主线程的id值是否也与LWP不同?这我们就要了解一个接口pthread_self(线程获取自己的id)
#include <iostream> #include <pthread.h> #include <string> #include <unistd.h> void *threadRoutine(void *args) { std::string name = static_cast<const char*>(args); while(true) { std::cout << "new thread, name: " << name << "thread id is " << pthread_self() << std::endl; sleep(1); } } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, (void*)"thread-1"); while(true) { std::cout << "main thread id " << pthread_self() << "sub thread id " << tid << std::endl; sleep(1); } return 0; }
主线程的id与新线程的id明显不是LWP,那是什么呢?我们以十六进制打印看看。
#include <iostream> #include <pthread.h> #include <string> #include <unistd.h> // 转换为十六进制 std::string ToHex(pthread_t tid) { char id[64]; snprintf(id, sizeof(id), "0x%lx", tid); return id; } void *threadRoutine(void *args) { std::string name = static_cast<const char*>(args); while(true) { std::cout << "new thread, name: " << name << "thread id is " << ToHex(pthread_self()) << std::endl; sleep(1); } } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, (void*)"thread-1"); while(true) { std::cout << "main thread id " << ToHex(pthread_self()) << std::endl; sleep(1); } return 0; }
现在,这个线程id是不是很像地址? 没错,线程id就是地址。
如果需要只终止某个线程而不终止整个进程,可以有三个方法:
#include <iostream> #include <pthread.h> #include <string> #include <unistd.h> // 转换为十六进制 std::string ToHex(pthread_t tid) { char id[64]; snprintf(id, sizeof(id), "0x%lx", tid); return id; } void *threadRoutine(void *args) { std::string name = static_cast<const char*>(args); int cnt = 5; while(cnt--) { std::cout << "new thread, name: " << name << "thread id is " << ToHex(pthread_self()) << std::endl; sleep(1); } return nullptr; // 线程终止 } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, (void*)"thread-1"); while(true) { std::cout << "main thread id " << ToHex(pthread_self()) << std::endl; sleep(1); } return 0; }
#include <iostream> #include <pthread.h> #include <string> #include <unistd.h> // 转换为十六进制 std::string ToHex(pthread_t tid) { char id[64]; snprintf(id, sizeof(id), "0x%lx", tid); return id; } void *threadRoutine(void *args) { std::string name = static_cast<const char*>(args); int cnt = 5; while(cnt--) { std::cout << "new thread, name: " << name << "thread id is " << ToHex(pthread_self()) << std::endl; sleep(1); } //return nullptr; // 线程终止 pthread_exit(nullptr); } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, (void*)"thread-1"); while(true) { std::cout << "main thread id " << ToHex(pthread_self()) << std::endl; sleep(1); } return 0; }
不能用exit函数来终止线程,这会导致这个进程被终止。
#include <iostream> #include <pthread.h> #include <string> #include <unistd.h> // 转换为十六进制 std::string ToHex(pthread_t tid) { char id[64]; snprintf(id, sizeof(id), "0x%lx", tid); return id; } void *threadRoutine(void *args) { std::string name = static_cast<const char*>(args); int cnt = 5; while(cnt--) { std::cout << "new thread, name: " << name << "thread id is " << ToHex(pthread_self()) << std::endl; sleep(1); } //return nullptr; // 线程终止 exit(13); } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, (void*)"thread-1"); while(true) { std::cout << "main thread id " << ToHex(pthread_self()) << std::endl; sleep(1); } return 0; }
我们知道子进程退出时会有僵尸状态,需要父进程来等待子进程。那线程也要被等待吗?是的,线程也要被等待。线程退出没有等待,也会导致类似进程的僵尸问题。线程通过等待获取新线程的返回值。
以从线程return为例。
#include <iostream> #include <pthread.h> #include <string> #include <unistd.h> // 转换为十六进制 std::string ToHex(pthread_t tid) { char id[64]; snprintf(id, sizeof(id), "0x%lx", tid); return id; } void *threadRoutine(void *args) { std::string name = static_cast<const char *>(args); int cnt = 5; while (cnt--) { std::cout << "new thread, name: " << name << "thread id is " << ToHex(pthread_self()) << std::endl; sleep(1); } return nullptr; // 线程终止 } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, (void *)"thread-1"); std::cout << "main thread id " << ToHex(pthread_self()) << std::endl; int n = pthread_join(tid, nullptr); std::cout << "main thread done , n : " << n << std::endl; return 0; }
对于一个线程,如果主线程要等待它,则会阻塞等待导致主线程自己的任务效率低。如果主线程不等待它,则可能导致类似僵尸进程的状态,使资源泄漏。这时我们就可以将线程分离。
线程可以设置为分离属性,一个线程如果被设置为分离属性,则该线程在退出之后,不需要其它执行流回收该线程资源,而是由操作系统进行回收。
分离的线程在同一个进程地址空间,相互的线程不会相互干扰,但是如果分离的线程崩溃,进程也会崩溃。
#include <iostream> #include <pthread.h> #include <string> #include <unistd.h> // 转换为十六进制 std::string ToHex(pthread_t tid) { char id[64]; snprintf(id, sizeof(id), "0x%lx", tid); return id; } void *threadRoutine(void *args) { std::string name = static_cast<const char *>(args); int cnt = 5; while (cnt--) { std::cout << "new thread, name: " << name << "thread id is " << ToHex(pthread_self()) << std::endl; sleep(1); } int a = 10; a /= 0; // 除0异常 return nullptr; // 线程终止 } int main() { pthread_t tid; pthread_create(&tid, nullptr, threadRoutine, (void *)"thread-1"); std::cout << "main thread id " << ToHex(pthread_self()) << std::endl; sleep(10); pthread_cancel(tid); // int n = pthread_join(tid, nullptr); // std::cout << "main thread done , n : " << n << std::endl; return 0; }
以上就是我对于线程控制的总结。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。