当前位置: 首页 > news >正文

济南专业网站设计华能电子商务平台

济南专业网站设计,华能电子商务平台,店铺推广软文范文,集团网站信息建设情况欢迎来到博主的专栏#xff1a;从0开始linux 博主ID#xff1a;代码小豪 文章目录 线程创建线程标识符线程参数多线程竞争资源 回收线程detach 线程退出pthread_cancel 线程创建 线程创建的函数为pthread_create。该函数是包含在posix线程库当中#xff0c;posix线程是C语言…欢迎来到博主的专栏从0开始linux 博主ID代码小豪 文章目录 线程创建线程标识符线程参数多线程竞争资源 回收线程detach 线程退出pthread_cancel 线程创建 线程创建的函数为pthread_create。该函数是包含在posix线程库当中posix线程是C语言处理线程的一个标准接口我们的进程创建、杀死和回收线程都可以通过posix库来完成完成这些函数包含在pthread.h库函数当中且大部分函数的名字都是以pthread开头但是要注意该库不是C语言标准库因此在使用pthread.h时要记得gcc/g的编译选项当中加上-lpthread。 pthread_create函数原型如下 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);在我们上一篇文章当中就使用了pthread_create函数但是博主当时并没有介绍完全这些参数接下来博主会使用三个pthread_create的例子让大家了解这些参数到底有什么用。 thread输出型参数创建一个pthread_t类型的变量并将其地址传入函数当中可以获得创建的线程的标识符attr设置线程的属性使用NULL表示线程设置成默认模式博主在本篇的例子中创建的都是默认模式的线程start_routine创建的线程的入口函数类似于线程自己的main函数arg传入给新线程的参数 线程标识符 在我们上一篇文章中我们提到linux将线程描述成一个轻量级进程LWP而且通过ps -aL可以查到轻量级进程的LWP号那么LWP号和线程标识符thread有什么关系呢 我们话不多说直接上例子。 void* routine1(void* arg)//新线程入口函数 {while(true){::sleep(1);} }int main() {pthread_t tid1;//线程标识符pthread_create(tid1,nullptr,routine1,nullptr);//创建新线程std::coutthread1 tid:tid1std::endl;while(true){sleep(1);}return 0; }接下来我们运行程序打开另外一个终端输入指令ps -aL | head -1;ps -aL | grep thread 这很明显了线程标识符和lwd号根本不是一个东西但是它们指向的都是同一个线程那么为什么要这样呢首先我们搞清楚一个点那就是pthread.h库是C语言封装的库不是操作系统的系统调用库因此C语言不必与操作系统在线程描述方面统一起来因此lwp号是操作系统用于辨识轻量级进程的而tid是C语言封装用来辨识线程的虽然在linux当中lwp和线程是同一个东西。但是不妨碍C语言将其视为其他属性。 线程参数 相信大家对pthread_create的参数有点疑惑吧第一个就是为什么我们写的start_routine线程的入口地址是要返回值为void*参数为void的回调函数返回值为void就算了那个void的参数到底有什么用啊我们平时创建出线程之后线程自己就跳转到回调函数当中去了那里轮得到我们自己使用。那么那个void的参数是什么怎么传别急听我娓娓道来。 在pthread_create当中存在一个void的参数也很存疑。即void *arg这个arg能传一个void类型的指针这个指针其实就是给回调函数start_routine的参数由于它是一个void*的指针因此我们可以用它指向任何数据整形、浮点型、任何的c/c的内置类型甚至可以是结构体类的对象。 我们简简单单的定义一个类 class thread { public:thread(std::string name,int a,int b):_name(name),_a(a),_b(b){;}void Excute(){std::cout_name-_a-_bstd::endl;}private:std::string _name;int _a;int _b; };接下来我们在main函数当中创建指向该类的对象的指针并且将该指针转为void*类型。作为参数传递给pthread_create。 int main() {pthread_t tid;thread* objptrnew thread(thread-1,11,26);pthread_create(tid,nullptr,routine1,(void*)objptr);while(true){sleep(1);}return 0; }而我们的routine1函数的参数不是要求是void类型的吗实际上该参数就是我们传入的objptr在routine1函数当中我们可以将void的指针转换成对象类型的指针thread*。这样我们就可以在线程执行的函数当中传入数据了。 void* routine1(void*arg) {thread* objstatic_castthread*(arg);//将void*类型转换成thread*obj-Excute();return nullptr; }接下来我们编译并执行该程序。 可以发现线程入口函数的参数其实就是我们传入给pthread_create的void*参数arg。 多线程竞争资源 线程之间的数据和代码是共享的这意味着我们在运行多线程的进程时会很难控制它们的运行情况。我们可以创建多个线程让他们共同对一个全局变量进行修改并且使用同一个函数进行打印看看会发生什么。 int shared_arg100;//共同使用的全局变量 void* sharedroutine(void* arg)//共同使用的函数 {std::string name(static_castchar*(arg));while(true){shared_arg;std::couttid:name arg:shared_argstd::endl;::usleep(10);} }int main() {pthread_t tid1;//线程1标识符pthread_t tid2;//线程1标识符pthread_t tid3;//线程1标识符pthread_t tid4;//线程1标识符pthread_create(tid1,nullptr,sharedroutine,(void*)thread-1);//创建新线程1pthread_create(tid2,nullptr,sharedroutine,(void*)thread-2);//创建新线程2pthread_create(tid3,nullptr,sharedroutine,(void*)thread-3);//创建新线程3pthread_create(tid4,nullptr,sharedroutine,(void*)thread-4);//创建新线程4swhile(true){sleep(1);}return 0; }我们编译并执行程序。 可以发现定义在全局当中变量shared_arg在所有的线程当中是共享的线程1对其修改后线程2、线程3、线程4的shared_arg也会跟着修改。那么此时有人就问了局部变量就不共享吗实际上也是共享的只是局部变量会存在于栈区间上而线程之间的栈区间是独立的除非你能让线程找到其他线程栈区间上的变量。但是博主想不到该怎么做haha。 而且我们发现线程之间既然发生了写入重复的错误那么这是如何导致的呢我们了解过线程会共享进程中的资源其中就包括文件资源而linux当中一切皆文件包括显示器因此如果我们向显示器写入数据如果线程1和线程2同时都在向显示器写入数据都会发现这种写入重复的情况。实际上不仅仅是显示器只要是多个线程向任何一种文件写入数据都有可能发生这种情况。 那么我们可以看到线程对于公开资源的使用其实是需要我们控制的因为你也不想发现程序执行完后明明逻辑写的对的但是一打开文件、发现写的数据都是乱七八糟的吧而线程的控制方法就是线程的互斥和同步这一点我们后面再说。 回收线程 由于线程有其独立的内核数据结构而内核数据结构是实打实存在于内存当中的因此如果当线程结束后没有对线程进行回收这个内核数据结构就会一直存在于内存当中造成内存泄漏这个道理和进程回收很像。 那么为什么线程不会自动回收呢这是因为我们的线程在结束之后是会有返回值的这个返回值可以被用户所使用因此操作系统不敢自动回收线程因为操作系统也不确定我们用户到底用不用这个返回值也不确定我们何时用线程的返回值。因此操作系统就只好一直帮助我们保存。直到用户主动回收为止。 那么线程回收的函数是什么 int pthread_join(pthread_t thread, void **retval);threadpthread_t类型的参数即我们想要回收的线程的线程描述符retval输出型参数还记的我们线程的入口函数其返回值是void*的吧这个retval就是接收线程的返回值的。 如果pthread_join正常回收其返回值为0若pthread_join回收异常则返回值为非0。但是博主在linux环境下没有找到引起pthread_join回收失败返回值为非0的情况。因为大部分时候如果线程出现了错误进程就直接被杀死了。那么博主就不试了。 要注意pthread_join会引起进程阻塞而且线程不能自己回收自己否则会造成deadlock死锁因为线程回收自己会导致阻塞而线程自己阻塞了就永远无法结束也就无法回收了不能回收又会一直阻塞……俄罗斯套娃 关于回收我们先简单认识这个函数线程到底如何回收会更好这一点我们后面再说。 话不多说直接上例子。 void* routine1(void*arg) {thread* objstatic_castthread*(arg);//将void*类型转换成thread*obj-Excute();return (void*)10; }我们接着用上面使用过的代码但是将其改造了一下routine1的返回值从nulllptr变成了arg。 int main() {pthread_t tid;thread* objptrnew thread(thread-1,11,26);pthread_create(tid,nullptr,routine1,(void*)objptr);void* threadretnullptr;int npthread_join(tid,threadret);if(n0) std::cout回收tidtid成功std::endl;unsigned long ret(unsigned long)threadret;std::coutretstd::endl;return 0; }在主线程当中我们用void**类型的参数接收routine1函数的返回值并且将其转换成unsigned long类型的变量。由于我们将其以void*类型返回了因此要转换成unsigned long之后才能看到返回值10。 接下来我们编译并运行。 可以发现确实能将线程回收而且routine1当中的返回值确实是能通过pthread_join获得。 通常情况下回收线程的工作都是交给主线程来完成如果主线程结束那么其他线就会继续运行因此大部分情况下主线程一定会比其他线程晚退出。 detach 由于主线程在回收线程时需要阻塞等待待回收的线程结束那么如果线程直接没有结束那么主线程就会一直等待这可能不是你想要的代码逻辑那么有没有办法让主线程等待线程时不阻塞等待。 线程有两种状态其中一种是joined也是我们创建新线程的默认状态还有一种叫做detach。我们可以使用函数pthread_detach使线程进入detach状态当线程处于detach状态下对于该线程的回收操作将会失效。 int pthread_detach(pthread_t thread);这个函数就不展示用例我们向pthread_detach函数传入线程标识符threadthread就会进入detach状态。处于detach状态的线程会在线程退出后自动被操作系统回收。如果我们尝试用pthread_join回收detach状态下的线程那么pthread_join只会返回一个非0的值说明回收线程的操作失败了。detach的线程是不会被其他线程回收的。 线程退出 线程的退出方法有两种一种是直接在线程的执行函数当中return这个方法我们一直在前面的例子当中使用因此不多bb。 第二种方法是在线程的执行函数使用pthread_exit函数。该函数原型如下 void pthread_exit(void *retval);retval线程结束的返回值在主线程当中可以使用pthread_joined接收到该返回值。 我们可以在线程的任意一个执行位置使用pthread_exit。当线程执行到该位置时就会直接退出这是和return结束线程不一样的地方pthread_exit可以在任意的位置退出即使线程处于调用中的函数。 void thread_to_do() {std::cout线程正在执行thread_todostd::endl;//如果真推出了显示器打印的信息只有这个pthread_exit((void*)10); }void* routine1(void*arg) {thread_to_do();std::cout线程从thread_todo执行回来std::endl;//如果没有退出那么还会打印这个信息return (void*)10; } 我们让线程在执行thread_to_do函数接着在thread_to_do函数当中调用pthread_exit函数如果真如我们所言pthread_t能让线程无论在执行任何函数时都能无视函数栈帧直接退出那么屏幕上只会打印出“线程正在执行……”。而不会打印后面的信息。 那么有人可能会这么觉得我们使用exit()函数是不是也能让线程退出呢这句话对但也不对对是因为当线程执行到exit时确实会退出但是不对的地方在于不仅仅执行exit()函数的线程会退出进程中所有的一切线程也会随之退出。因此exit其实是不能作为线程退出来使用的因为它本身的作用是让进程退出而非线程。因此在程序当中使用exit函数时一定要注意你退出的要是进程还是线程。 pthread_cancel 严格来说pthread_cancel函数并不属于线程退出而是将一个特定的线程直接删除被删除的线程需要被回收一般这个工作都是让主线程来完成。我们先来看看pthread_cancel的函数原型。 int pthread_cancel(pthread_t thread);thread这是一个pthread_t类型的参数即线程标识符删除thread对应线程标识符的线程。 这个功能很简单但是我们有一个问题是要解决的那就是被删除的线程不是要回收吗那么回收不就是能接收到线程的返回值那么它的返回值是多少呢 void* routine1(void*arg) {thread* objstatic_castthread*(arg);//将void*类型转换成thread*while(true){obj-Excute();sleep(1);}return (void*)10; }int main() {pthread_t tid;thread* objptrnew thread(thread-1,11,26);pthread_create(tid,nullptr,routine1,(void*)objptr);sleep(1);pthread_cancel(tid);//将tid对应的线程删除sleep(5);void* threadretnullptr;int npthread_join(tid,threadret);if(n0) std::cout回收tidtid成功std::endl;long ret(long)threadret;std::coutretstd::endl;return 0; }运行结果为 pthread_cancel结束的进程其返回值为PTHREAD_CANCELED;这是一个宏我们可以找到这个宏定义。 也就是说只要被pthread_cancel结束的线程其返回值只会是-1。
http://www.dnsts.com.cn/news/103315.html

相关文章:

  • 做网站建设的一般在哪儿找贵州住房和城乡建设厅官方网站
  • 新乡网站建设策划企业网站托管的方案
  • 最近三天国内重大新闻seo专业培训网络班
  • 网站内容全屏截屏怎么做广东建设企业网站怎么样
  • 什么外贸网站开发客户怎么设置微信公众号
  • 成功的网站建设网站支付宝接口代码
  • 国内php开发的电商网站有哪些vs做网站怎样添加图片
  • 网站可以给pdf做笔记建网站做seo
  • 网站空间多少广告投放平台代理
  • 网站建设合同按什么交印花税网站里添加聊天框怎么做
  • 建设银行首页 网站自己怎么做wap网站
  • 怎样网站优化公司分析竞争对手的网站
  • 鞋材东莞网站建设wordpress调用分类目录
  • 乾安网站建设仿牌网站服务器
  • 集团公司网站方案韶关网站建设的公司
  • 微网站有哪些提供网站设计方案公司
  • 网站分享图片怎么做模板建站和自助建站
  • 网站如何选择关键词推荐邵阳网站建设
  • 厦门做商城网站做外汇网站
  • 做常识的网站wordpress提示安装
  • 郑州网站建设网络推广文化礼堂建设情况网站
  • 有没有做奥数题的网站网站建设工作总结6
  • 大兴黄村网站建设公司三网合一网站
  • 长沙 php企业网站系统安宁网站建设熊掌
  • 专业设计网站的公司今天全国生猪价格一览表
  • 网站seo推广计划媒体资源网
  • 如何看网站关键词旅游模板网站
  • 郑州门户网站建设10m带宽做下载网站
  • 网站自定义title项目加盟代理商
  • 精通网站建设 百度云网站制作的要求