pthread.h 相关函数使用方法集锦之线程操作

前言

pthread(POSIX thread),简称为pthread,是线程的POSIX标准,在类Unix操作系统中(Unix、Linux、Mac OS X等),都是用pthread作为操作系统的线程。<pthread.h>作为其编程标准的头文件,本文探讨里面的常用函数意义以及使用方法。

线程

pthread_create

pthread_create是UNIX环境创建线程函数 。共四个参数:

  • 第一个参数为指向线程标识符的指针,type: pthread_t*
  • 第二个参数用来设置线程属性
  • 第三个参数是线程运行函数的起始地址, type: (void*)(*)(void*)
  • 第四个参数是运行函数的参数,type: void *

无参数函数

pthread_create创建线程执行无参数函数的demo。

#include <pthread.h>
#include <iostream>

using namespace std;

// 注意函数定义: void* (*)(void*)
void* test(void *ptr){
    cout << "hello world." << endl;
}

int main()
{
    pthread_t tid; // 声明线程id typedef unsigned
    pthread_create(&tid, NULL, test, NULL); // 创建线程
    pthread_join(tid, NULL);
}

带参数函数

pthread_create只支持单个指针,只能传一个参数;如果要传多个参数,封装成结构体形式。

#include <pthread.h>
#include <string.h>
#include <iostream>

using namespace std;

struct Param{
    string name;
    int age;
};

void* param_test(void* p){
    Param *param = (Param*)p;
    cout << "hello " << param->name << endl;
    cout << "age = " << param->age << endl;
}

int main()
{
    pthread_t tid;
    Param *ptr = new Param();
    ptr->name = "pthread";
    ptr->age = 20;
    pthread_create(&tid, NULL, param_test, ptr);
    pthread_join(tid, NULL);
}

类成员函数

调用类成员函数的方式有点复杂,需要另外一个函数封装,函数参数显式传入类实例指针,之后便与调用带参数函数的方法类似。

#include <pthread.h>
#include <string.h>
#include <iostream>

using namespace std;

class PthreadClass{
    public:
        void say_hello(){
            cout << "hello world" << endl;
        }
};

// 封装 传入类实例指针
void* class_test(void *p){
    PthreadClass *ptr = (PthreadClass*)p;
    // 实例调用
    ptr->say_hello();
}

int main()
{
    PthreadClass *ptr = new PthreadClass();
    pthread_t tid;
    pthread_create(&tid, NULL, class_test, ptr);
    pthread_join(tid, NULL);
}

pthread_join

pthread_join用来等待一个线程的结束,线程间同步的操作 ,共两个参数:

  • 第一个参数为线程标识符,即线程ID,type: pthread_t
  • 第二个参数retval为用户定义的指针,用来存储线程的返回值,type: void**
#include <pthread.h>
#include <iostream>

using namespace std;

class PthreadClass{
    public:
        void say_hello(){
            cout << "hello world" << endl;
        }
};

int retval = -1;
void* class_test(void *p){
    PthreadClass *ptr = (PthreadClass*)p;
    ptr->say_hello();
    retval = 0;
    return (void*)&retval;
}

int main()
{
    PthreadClass *ptr = new PthreadClass();
    pthread_t tid;
    pthread_create(&tid, NULL, class_test, ptr);
    /**
     * 等待结束
     * 注意,如果不加pthread_join,那么有可能新线程还没等创建出来就已经结束程序
     */
    void *retval;
    pthread_join(tid, &retval);
    cout << *(int*)retval << endl;
}

pthread_detach

创建一个线程默认的状态是joinable, 如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码),所以创建线程者应该pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于wait,waitpid)。
但是调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞在有些情况下我们并不希望如此,比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时可以在子线程中加入代码pthread_datach脱离阻塞,这时候子线程状态为detached,运行结束后会自动释放资源。

pthread_detach只有一个参数:

  • 第一个参数为线程标识符,即线程ID,type: pthread_t
#include <pthread.h>
#include <unistd.h>
#include <iostream>

using namespace std;

void* test(void *p){
    sleep(1000);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, test, NULL);
    pthread_detach(tid);
    for(int i = 0; i < 1000; i ++){
        sleep(1);
        cout << "sleep " << i << " second." << endl;
    }
}

这里使用pthread_detach,就不会阻塞在testsleep内,会跑进for循环,如果使用的是pthread_join的话,则会等到test函数运行完毕才会往下跑。

pthread_self

pthread_self函数无参数,作用是获取当前线程id


#include <pthread.h>
#include <unistd.h>
#include <iostream>

using namespace std;

void* test(void *p){
    cout << 'child thread: ' << pthread_self() << endl;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, test, NULL);
    pthread_detach(tid);
    cout << 'main thread: ' << pthread_self() << endl;
    for(int i = 0; i < 1000; i ++){
        sleep(1);
    }
}

以上代码会输出,主线程和子线程不同的线程id:

main thread: 1
child thread: 2

pthread_once

pthread_once在多线程环境中只执行一次,将多线程环境中只需要执行一次的操作交给 pthread_once执行,则会线程安全的只执行一次。

pthread_once有两个参数:

  • 第一个参数为pthread_once_t变量,相当于标识符,type: pthread_once_t
  • 第二个参数为无参数函数指针,type: void(*func)(void)

以下例子,once_run只会执行一次,但每次执行的结果可能不同,once_run可能在线程1执行,也可能在线程2执行。


#include<iostream>
#include<pthread.h>
#include <unistd.h>
using namespace std;

pthread_once_t once = PTHREAD_ONCE_INIT;

void once_run(void)
{
    cout<<"once_run in thread "<<(unsigned int )pthread_self()<<endl;
}

void * child1(void * arg)
{
    pthread_t tid =pthread_self();
    cout<<"thread "<<(unsigned int )tid<<" enter"<<endl;
    pthread_once(&once,once_run);
    cout<<"thread "<<tid<<" return"<<endl;
}


void * child2(void * arg)
{
    pthread_t tid =pthread_self();
    cout<<"thread "<<(unsigned int )tid<<" enter"<<endl;
    pthread_once(&once,once_run);
    cout<<"thread "<<tid<<" return"<<endl;
}

int main(void)
{
    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,child1,NULL);
    pthread_create(&tid2,NULL,child2,NULL);
    sleep(10);
    cout<<"main thread exit."<<endl;
    return 0;
}

输出:

thread 2 enter
once_run in thread 2
thread 2 return
thread 3 enter
thread 3 return

pthread_cancel

发送一个cancel信号给指定线程,只有一个参数线程标识符,类型为pthread_t,用法简单,这里不多做介绍,要了解原理的同学可以在linux下用man命令了解更多。

man pthread_cancel

pthread_kill

向某个线程传递一个信号 ,需要在创建的线程中使用signal(SIGKILL,sig_handler)函数处理信号,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。

pthread_kill两个参数:

  • 第一个参数为线程标记符,type: pthread_t
  • 第二个参数为Linux定义的信号,type:int

原文链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容