C/C++ pthead

2024-06-21 15:38
文章标签 c++ pthead

本文主要是介绍C/C++ pthead,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考:

Pthread:http://baike.baidu.com/link?url=h1WNvWuntrqvtmElbY_i383deAS986IuyoBzwf2KZVOTw_B-fgNpgR9uBQN5mWdpNe9IAUezMJnrzc6s_-xpx_

POSIX Threads:https://en.wikipedia.org/wiki/POSIX_Threads

C++ Multithreading:http://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm

POSIX thread (pthread) libraries:http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html#BOOKS

POSIX Threads Programming:https://computing.llnl.gov/tutorials/pthreads/

多线程:http://wiki.jikexueyuan.com/project/cplusplus/multithreading.html


##############################################################



Pthread,全称为POSIX thread,可运行于所有类Unix操作系统上,在Windows平台上也可使用 - POSIX Threads for Win32


进行Pthread多线程编程时,所使用头文件为:

#include <pthread.h>



##############################################################


前言

1.线程创建(pthread_create)

2.传递参数给线程程序

3.获取线程返回的参数

4.聊聊线程属性pthread_attr_t

5.也聊一聊线程ID - pthread_t

6.线程终止

7.互斥锁 - mutexes

8.条件变量 - condition variables

未完待续


###############################################################3


当期运行环境:Ubuntu14.04


线程操作包括线程创建(thread creation),中止(termination),同步(synchronization(joins, blocking)),调度(scheduling),数据管理(data management)以及进程交互(process interaction)。

线程之间没有任何隐式的继承关系(no implied hierarchy),即线程并不知道是哪一个线程创建了它。

同一个进程中,各线程共享:

1)进程指令(process instructions)

2)大部分的数据(most data)

3)open files(descriptors)

4)信号以及信号处理器(signals and signal handlers)

5)当前工作路径(current working directory)

6)用户id和组id(user and group id)

而各线程之间独自拥有:

1)线程id(thread id)

2)寄存器组和堆栈指针(set of registers,stack pointer)

3)用于局部变量的堆栈和返回地址(stack for local variabels,return addresses)

4)信号掩码(signal mask)

5)优先级(priority)

6)返回值(return value:errno)

在pthread程序中,如果运行成功,则返回0。


1.线程创建(pthread_create)

函数格式:

    int pthread_create(pthread_t * thread, const pthread_attr_t * attr,void * (*start_routine)(void *), void *arg);

pthread参数介绍

pthread_t:用于声明线程id

pthread_attr_t:用于保存线程属性


函数功能:创建一个新线程,并运行该线程中的程序

函数参数

thread:pthread_t指针,新线程创建后,会得到一个独一无二的标识符,将保存在该指针中

attr:pthread_attr_r指针,用于设置新线程的属性,默认值为NULL

start_routine:在线程中启动执行的程序

arg:start_routine唯一一个可以使用的参数,如果不使用,则设为NULL


程序1:开启两个新线程,一个线程打印"Hello World"5次,另一个打印"Hi zj"5次,每次打印间隔1秒(没有函数参数,也不设置线程属性)

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* thread1(void*);
void* thread2(void*);int main(int argc, char* argv[]) {pthread_t t[2];int iret1, iret2;iret1 = pthread_create(&t[0], NULL, thread1, NULL);if (iret1) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret1);exit(EXIT_FAILURE);}iret2 = pthread_create(&t[1], NULL, thread2, NULL);if(iret2) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret2);exit(EXIT_FAILURE);}printf("pthread_create() for thread 1 returns: %d\n", iret1);printf("pthread_create() for thread 2 returns: %d\n", iret2);return 0;
}void* thread1(void*) {for (int i=0; i<5; i++) {cout <<"Hello World" <<endl;sleep(1);}
}void* thread2(void*) {for (int i=0; i<5; i++) {cout <<"Hi zj" <<endl;sleep(1);}
}

结果:



从上面的运行结果可以看出,

a)pthread_create创建成功后返回值是0;

b)线程创建成功后,main函数和线程中的程序并行运行;

c)线程没有运行完就结束了。

原因:我们称main函数运行在主线程中,在main函数开启新线程后,主线程和子线程并行运行,如果主线程运行结束(即main函数运行完成),则自动结束整个进程,即所有子线程也自动结束。

解决方法:有两种

方法1:使用函数pthread_exit

方法2:使用函数pthread_join


pthread_exit函数格式:

void pthread_exit(void *retval);

参数retval:pthread_exit()的返回值

函数功能:终止线程,如果在主线程(即main函数)中使用此函数,则主线程结束后,其他线程还会继续执行;否则,主线程终止后,其他子线程自动终止。


程序2:使用函数pthread_exit

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* thread1(void*);
void* thread2(void*);int main(int argc, char* argv[]) {pthread_t t[2];int iret1, iret2;iret1 = pthread_create(&t[0], NULL, thread1, NULL);if (iret1) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret1);exit(EXIT_FAILURE);}iret2 = pthread_create(&t[1], NULL, thread2, NULL);if(iret2) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret2);exit(EXIT_FAILURE);}printf("pthread_create() for thread 1 returns: %d\n", iret1);printf("pthread_create() for thread 2 returns: %d\n", iret2);pthread_exit(NULL);
//    return 0;
}void* thread1(void*) {for (int i=0; i<5; i++) {cout <<"Hello World" <<endl;sleep(1);}
}void* thread2(void*) {for (int i=0; i<5; i++) {cout <<"Hi zj" <<endl;sleep(1);}
}



pthread_join函数格式:

int pthread_join(pthread_t th, void **thread_return);

参数th:线程id

参数thread_return:如果线程th返回值非空,可以从这里获得

函数功能:挂起当前线程,直到线程th结束


程序3:使用函数pthread_join

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* thread1(void*);
void* thread2(void*);int main(int argc, char* argv[]) {pthread_t t[2];int iret1, iret2;iret1 = pthread_create(&t[0], NULL, thread1, NULL);if (iret1) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret1);exit(EXIT_FAILURE);}iret2 = pthread_create(&t[1], NULL, thread2, NULL);if(iret2) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret2);exit(EXIT_FAILURE);}pthread_join(t[0], NULL);pthread_join(t[1], NULL);printf("pthread_create() for thread 1 returns: %d\n", iret1);printf("pthread_create() for thread 2 returns: %d\n", iret2);//    pthread_exit(NULL);return 0;
}void* thread1(void*) {for (int i=0; i<5; i++) {cout <<"Hello World" <<endl;sleep(1);}
}void* thread2(void*) {for (int i=0; i<5; i++) {cout <<"Hi zj" <<endl;sleep(1);}
}



注意:C/C++对于pthread_create的语法并不全部一致

在上面使用的pthread_create,可以通过C和C++的编译:

pthread_create(&t[1], NULL, thread2, NULL);

但在C语言中还有一种pthread_create的用法:

pthread_create(&t[1], NULL, (void*)&thread1, NULL);
这种用法仅能用在C语言中,不能通过C++的编译。


所以,为了C和C++的共同使用,统一使用上面第一种的用法!!!


###################################################################


2.传递参数给线程程序


在pthread_create函数中,我们可以使用第四个参数arg给第三个参数start_routine函数传递参数

程序1:传递数字

#include <iostream>
#include <pthread.h>
using namespace std;void* thread(void* arg);int main(int argc, char* argv[]) {pthread_t pt;int iret;int num = 10;iret = pthread_create(&pt, NULL, thread, (void*)&num);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}pthread_join(pt, NULL);cout <<"endl..." <<endl;return 0;
}void* thread(void* arg) {int num;num = *(int*)arg;cout <<"num: " <<num <<endl;
}


程序2:传递字符

#include <iostream>
#include <pthread.h>
using namespace std;void* thread(void* arg);int main(int argc, char* argv[]) {pthread_t pt;int iret;char ch = 'a';iret = pthread_create(&pt, NULL, thread, (void*)&ch);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}pthread_join(pt, NULL);cout <<"endl..." <<endl;return 0;
}void* thread(void* arg) {char ch;ch = *(char*)arg;cout <<"ch: " <<ch <<endl;
}



程序3:传递字符数组

#include <iostream>
#include <pthread.h>
using namespace std;void* thread(void* arg);int main(int argc, char* argv[]) {pthread_t pt;int iret;char* ch = "hello zj";iret = pthread_create(&pt, NULL, thread, (void*)ch);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}pthread_join(pt, NULL);cout <<"endl..." <<endl;return 0;
}void* thread(void* arg) {char *ch;ch = (char*)arg;cout <<"ch: " <<ch <<endl;
}



程序4:传递字符串string

#include <iostream>
#include <pthread.h>
using namespace std;void* thread(void* arg);int main(int argc, char* argv[]) {pthread_t pt;int iret;string str = "hi zj";iret = pthread_create(&pt, NULL, thread, (void*)&str);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}pthread_join(pt, NULL);cout <<"endl..." <<endl;return 0;
}void* thread(void* arg) {string str;str = *(string*)arg;cout <<"str: " <<str <<endl;
}



当你需要传递多个参数时,可以定义一个结构体进行传输

程序5:传递结构体

#include <iostream>
#include <pthread.h>
#include <string.h>
using namespace std;struct aarg {int num;char ch[3];string str;
};void* thread(void* arg);int main(int argc, char* argv[]) {pthread_t pt;int iret;struct aarg stru;stru.num = 3;strcpy(stru.ch, "hi");stru.str = "hello zj";iret = pthread_create(&pt, NULL, thread, (void*)&stru);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}pthread_join(pt, NULL);cout <<"endl..." <<endl;return 0;
}void* thread(void* arg) {struct aarg stru;stru = *(struct aarg*)arg;cout <<"stru.num: " <<stru.num <<endl;cout <<"stru.ch: " <<stru.ch <<endl;cout <<"stru.str: " <<stru.str <<endl;
}


########################################################


3.获取线程返回的参数


线程程序可以有返回值,有两种方法获取返回值

1)利用start_routine函数参数返回

定义一个结构体,结构体中一部分参数用于输入,一部分参数用于返回

#include <iostream>
#include <pthread.h>
#include <string.h>
using namespace std;#define IN
#define OUTstruct argg {IN int num;IN char ch;IN string str;OUT char arr[10];
};void* thread(void* arg);int main(int argc, char* argv[]) {pthread_t pt;int iret;struct argg stru;stru.num = 10;stru.ch = 'a';stru.str = "hello zj";memset(stru.arr, '\0', sizeof(char)*10);iret = pthread_create(&pt, NULL, thread, (void*)&stru);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}pthread_join(pt, NULL);cout <<"stru.arr: " <<stru.arr <<endl;cout <<"endl..." <<endl;return 0;
}void* thread(void* arg) {struct argg *stru;stru = (struct argg*)arg;cout <<"stru->num: " <<stru->num <<endl;cout <<"stru->ch: " <<stru->ch <<endl;cout <<"stru->str: " <<stru->str <<endl;strcpy(stru->arr, "hello world");cout <<"stru->arr: " <<stru->arr <<endl;
}



注意:

a)宏定义参数IN和OUT是为了表明该参数是输入还是输出的;

b)在线程函数中,使用指针进行传递。


2)利用pthread_join函数参数返回

如果线程已pthread_exit结束,则pthread_exit中的参数void* retval会存储在pthread_join中的参数void** thread_return中

目前仅发现可以传输0或者字符串

程序1:传输0

#include <iostream>
#include <pthread.h>
using namespace std;void* thread(void*);int main(int argc, char* argv[]) {pthread_t pt;int iret;void* res;iret = pthread_create(&pt, NULL, thread, NULL);pthread_join(pt, &res);cout <<"res: " <<res <<endl;cout <<"Endl..." <<endl;return 0;
}void* thread(void *) {cout <<"Hello zj" <<endl;pthread_exit(0);
}



注意:使用    pthread_exit(NULL); 也会有同样效果


程序2:传递字符串

#include <iostream>
#include <pthread.h>
using namespace std;void* thread(void*);int main(int argc, char* argv[]) {pthread_t pt;int iret;void* res;iret = pthread_create(&pt, NULL, thread, NULL);pthread_join(pt, &res);cout <<"res: " <<(char*)res <<endl;cout <<"Endl..." <<endl;return 0;
}void* thread(void *) {cout <<"Hello zj" <<endl;char* res = "hi zj";pthread_exit(res);
}



综合上述两种方法,获取线程返回的参数时建议使用第一种


###################################################################3


4.聊聊线程属性pthread_attr_t


当创建一个新线程时,会依据一些默认的属性,如果程序员想要修改,可以通过线程属性pthread_attr_t来改变线程

线程属性包括:

a)是否独立(detached or joinable state)

b)调度继承(scheduling inheritance,应该是说优先级)

c)调度测略(scheduling parameters)

d)调度的竞争范围(scheduling contention scope)

e)栈大小(stack size)

f)栈地址(stack address)

g)堆栈保护大小(stack guard (overflow)size)


当你想要把线程加入到pthread_join,这就要求该线程是joinable。大部分系统默认线程创建时是joinable状态,我们也可以使用函数pthread_attr_init()和函数pthread_attr_destroy()显示设置/销毁线程属性


函数介绍

pthread_attr_init:

int pthread_attr_init(pthread_attr_t *attr);
参数attr:指向一个线程属性结构的指针

返回值:成功返回0,失败返回错误代码

函数功能:初始化线程对象的属性


pthread_attr_destroy:

int pthread_attr_destroy(pthread_attr_t *attr);
函数功能:销毁参数attr


pthread_attr_setdetachstate:

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

参数detachstate:有两种选择 - PTHREAD_CREATE_DETACHED(设置属性为detached状态);PTHREAD_CREATE_JOINABLE(设置属性为joinable状态)

pthread_attr_getdetachstate:

int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);

参数detachstate:用于获取该属性的detach状态


pthread_detach:

int pthread_detach(pthread_t thread);
函数功能:不论之前属性设置,运行此函数后,线程thread属性变成PTHREAD_CREATE_DETACHED

程序:显示设置线程属性为Joinable

#include <iostream>
#include <pthread.h>
using namespace std;void* thread(void*);int main(int argc, char* argv[]) {pthread_t pt;pthread_attr_t attr;int iret;int detachstate;/* Initialize and set thread detached attribute */pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);iret = pthread_create(&pt, &attr, thread, NULL);if (iret) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret);exit(EXIT_FAILURE);}pthread_attr_getdetachstate(&attr, &detachstate);cout <<"detachstate: " <<detachstate <<endl;cout <<"PTHREAD_CREATE_JOINABLE: " <<PTHREAD_CREATE_JOINABLE <<endl;cout <<"PTHREAD_CREATE_DETACHABLE: " <<PTHREAD_CREATE_DETACHED <<endl;pthread_exit(NULL);
}void* thread(void *) {
//    cout <<"Hello thread" <<endl;
}



注意:如果pthread_join中使用的线程,显式设置属性为Joinable,这样可提高可移植性。对于不需要使用的线程,设置为Detached属性,一些系统资源可能会被释放。


#############################################################


5.也聊一聊线程ID - pthread_t


pthread_t是一个无符号长整数类型(unsigned long int defined in bits/pthreadtypes.h)

可以在线程内部获取线程id


pthread_self:

pthread_t pthread_self(void);

函数功能:获取当前线程的线程标识符(thread identifier)


程序:获取当前线程标识符

#include <iostream>
#include <pthread.h>
using namespace std;void* thread(void*);int main(int argc, char* argv[]) {pthread_t pt;int iret;iret = pthread_create(&pt, NULL, thread, NULL);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}pthread_join(pt, NULL);cout <<"pthread_t: " <<pt <<endl;pthread_exit(NULL);
}void* thread(void *) {pthread_t pt;pt = pthread_self();cout <<"thread - pt: " <<pt <<endl;
}



如果要判断两个线程是否相同时,不应该直接使用等于号(=)进行比较,而应该使用pthread_equal函数

pthread_equal:

int pthread_equal(pthread_t t1, pthread_t t2);

函数返回值:如果两个线程id相同,则返回非零值(nonzero value);否则,返回0。


###################################################3


6.线程终止


在以下几种情况下,线程会终止运行:

1)线程中的程序正常返回,即正常结束了;

2)线程中调用了子程序pthread_exit;

3)线程被另一个线程通过调用程序pthread_cancel取消了;

4)调用函数exec()或者exit(),整个进程结束了;

5)main()运行结束,并且没有显示调用程序pthread_exit


 上面已经了解了pthread_exit,接下来介绍pthread_cancel函数


pthread_cancel函数格式:

int pthread_cancel(pthread_t thread);

函数功能:向线程thread发送一个取消请求。目标线程thread是否执行这个取消请求,以及何时执行取决于该线程的两个属性:1)取消状态(cancelability state);2)取消类型(cancelability type)


想要设置线程的取消状态(cancelability state),可以通过函数pthread_setcancelstate


pthread_setcancelstate函数格式:

int pthread_setcancelstate(int state, int *oldstate);

函数参数

state:设置线程的取消状态(cancelability state)。有两个可选值:1)PTHREAD_CANCEL_ENABLE(默认);2)PTHREAD_CANCEL_DISABLE

oldstate:保存该线程之前的状态,也可以设置为NULL。

函数功能:设置调用该函数的线程的取消状态(cancelability state)


如果线程是PTHREAD_CANCEL_DISABLE的取消状态,但pthread_cancel函数调用它时,该取消请求会保存在队列中,直到该线程设置为PTHREAD_CANCEL_ENABLE的取消状态。

如果线程允许取消请求运行,那么取消类型(cancelability type)决定了取消请求是否能运行


想要设置线程的取消类型(cancelability type),可以通过函数pthread_setcanceltype


pthread_setcanceltype函数格式:

int pthread_setcanceltype(int type, int *oldtype);

函数参数:

type:设置线程的取消类型(cancelability type)。有两个可选值:1)PTHREAD_CANCEL_DEFERRED(默认值);2)PTHREAD_CANCEL_ASYNCHRONOUS

oldtype:保存该线程之前的类型,也可以设置为NULL。

函数功能:设置调用该函数的线程的取消类型(cancelability type)

如果取消类型是PTHREAD_CANCEL_DEFERRED,那么取消操作会被推迟到下一个取消点,即该线程调用下一个函数时。

如果取消类型是PTHREAD_CANCEL_ASYNCHRONOUS,则即时退出。


参考:

pthread_cancel:http://baike.baidu.com/link?url=YVRmmVKhsp-g0Mymw8YqERgq6_NOktfhyv11r3M16P5PmrQSZx8Y0ydjSAe_9k0NFBQdgjUlCjjzJlfCUET-s93yWeY38bJdRoDyfEYDU_e

man pthread_cancel

man pthread_setcancelstate

man pthread_setcanceltype


程序1:在main函数中取消一个线程(取消状态和取消类型默认)

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* thread(void*);int main(int argc, char* argv) {pthread_t pt;int iret;iret = pthread_create(&pt, NULL, thread, NULL);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}for (int i=0; i<3; i++) {cout <<"Hello zj\n";sleep(1);}pthread_cancel(pt);pthread_join(pt, NULL);cout <<"endl...\n";return 0;
}void* thread(void *) {for (int i=0; i<10; i++) {cout <<"Hello World\n";sleep(1);}
}


注意:在执行pthread_cancel函数后,最好执行pthread_join函数,等待指定的线程已经完全退出以后,再继续执行


程序2:在main函数中取消一个线程(取消状态设置为disable)

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* thread(void*);int main(int argc, char* argv) {pthread_t pt;int iret;iret = pthread_create(&pt, NULL, thread, NULL);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}for (int i=0; i<3; i++) {cout <<"Hello zj\n";sleep(1);}pthread_cancel(pt);pthread_join(pt, NULL);cout <<"endl...\n";return 0;
}void* thread(void *) {int oldstate;pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);cout <<"oldstate: " <<oldstate <<endl;cout <<"PTHREAD_CANCEL_DISABLE: " <<PTHREAD_CANCEL_DISABLE <<endl;cout <<"PTHREAD_CANCEL_ENABLE: " <<PTHREAD_CANCEL_ENABLE <<endl;for (int i=0; i<10; i++) {cout <<"Hello World\n";sleep(1);}
}



程序3:在main函数中取消一个线程(取消类型设置为asynchronous)

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* thread(void*);int main(int argc, char* argv) {pthread_t pt;int iret;iret = pthread_create(&pt, NULL, thread, NULL);if (iret) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret);exit(EXIT_FAILURE);}for (int i=0; i<3; i++) {cout <<"Hello zj\n";sleep(1);}pthread_cancel(pt);pthread_join(pt, NULL);cout <<"endl...\n";return 0;
}void* thread(void *) {int oldtype;pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);cout <<"oldtype: " <<oldtype <<endl;cout <<"PTHREAD_CANCEL_DERERRED: " <<PTHREAD_CANCEL_DEFERRED <<endl;cout <<"PTHREAD_CANCEL_ASYNCHRONOUS: " <<PTHREAD_CANCEL_ASYNCHRONOUS <<endl;for (int i=0; i<10; i++) {cout <<"Hello World\n";sleep(1);}
}



程序4:在子线程中取消另一个子线程

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;void* thread(void*);
void* thread_cancel(void*);int main(int argc, char* argv) {pthread_t pt1, pt2;int iret1, iret2;iret1 = pthread_create(&pt1, NULL, thread, NULL);if (iret1) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret1);exit(EXIT_FAILURE);}iret2 = pthread_create(&pt2, NULL, thread_cancel, &pt1);if (iret2) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret2);exit(EXIT_FAILURE);}pthread_join(pt1, NULL);pthread_join(pt2, NULL);cout <<"endl...\n";return 0;
}void* thread(void *) {int oldtype;pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);cout <<"oldtype: " <<oldtype <<endl;cout <<"PTHREAD_CANCEL_DERERRED: " <<PTHREAD_CANCEL_DEFERRED <<endl;cout <<"PTHREAD_CANCEL_ASYNCHRONOUS: " <<PTHREAD_CANCEL_ASYNCHRONOUS <<endl;for (int i=0; i<10; i++) {cout <<"Hello World\n";sleep(1);}
}void* thread_cancel(void *arg) {pthread_t *pt;pt = (pthread_t*)arg;for (int i=0; i<3; i++) {cout <<"Hello zj\n";sleep(1);}pthread_cancel(*pt);pthread_join(*pt, NULL);cout <<"ok\n";
}



##################################################################


线程同步


线程库提供了3种同步的机制

1.mutexes - 相互排斥锁(mutual exclusion lock),我称之为互斥锁:用于阻塞其他线程对于变量的访问。它强制了线程对于单个变量或者一组变量的互斥访问(exclusive access)

2.joins - 使线程停止运行,等待其他线程完成或者终止

3.condition variables


以下部分使用man手册进行查看时,可能没有相关内容,需要安装

sudo apt-get install manpages-posix-dev

参考:

man pthread_mutex_init 或 man pthread_mutex_lock 没有结果的解决办法,pthreadmutexinit - http://www.bkjia.com/ASPjc/869106.html


##########################################################################


7.互斥锁 - mutexes


mutex是“mutual exclusion”(相互排斥)的缩写(abbreviation)。它被用于防止多个线程同时对同一内存地址进行操作而导致的数据的不一致,或者被用于设置期待的对内存的操作顺序。当多个线程对同一内存区域进行操作时,经常会发生竞态条件(contention or race condition),而计算的结果依赖于执行的顺序,使用mute可以让我们得到想要的执行的顺序。

当多个线程同时对一个mutex对象进行锁定时,仅有一个线程会成功,而其他线程将由此阻塞,直到某个线程解锁后,另一个线程会得到mutex对象,依次解锁所有的线程。

mutex对象仅能用于同一进程中的线程之间,而不能在多个进程中使用。


最常见的一种使用Mutex的场景是更新一个全局变量。

典型的使用mutex的顺序如下:

1)创建和初始化一个mutex变量

2)多个线程试图去锁定这个mutex变量

3)仅有其中一个成功

4)成功的线程可以继续执行

5)结束执行后,解锁

6)另一个线程锁定了这个mutex变量,重复这一过程

7)最后,销毁这个Mutex变量


mutex对象的类型名为pthread_mutex_t,并且必须在使用前被初始化。


初始化mutex对象有两种方法:

一. 静态方法。比如

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

二. 动态方法。使用函数pthread_mutex_init()

pthread_mutex_init函数格式:

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
使用动态方法可以设置mutex变量的属性,该参数也可以设置为NULL,表示使用默认设置


如果想要设置attr,还需要使用函数pthread_mutexattr_init()和函数pthread_mutexattr_destroy()进行初始化和销毁属性


当mutex变量不再需要时,必须进行销毁,使用函数pthread_mutex_destroy()

pthread_mutex_destroy函数格式:

int pthread_mutex_destroy(pthread_mutex_t *mutex);


得到一个mutex变量后,我们使用函数pthread_mutex_lock和函数pthread_mutex_unlock进行锁定和解锁操作

pthread_mutex_lock函数格式:

int pthread_mutex_lock(pthread_mutex_t *mutex);

pthread_mutex_unlock函数格式:

int pthread_mutex_unlock(pthread_mutex_t *mutex);


多个线程同时对一个mutex对象调用pthread_mutex_lock函数时,仅有一个线程能够成功锁定,此时其余线程会阻塞在这里。如果线程不想阻塞,可以使用函数pthread_mutex_trylock函数,如果遇到mutex已被锁定,它将返回一个"busy"的错误码,而不会造成阻塞。

pthread_mutex_trylock函数格式:

int pthread_mutex_trylock(pthread_mutex_t *mutex);


 程序:两个线程同时访问一个全局变量

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;void* functionC(void*);int main(int argc, char* argv[]) {pthread_t pt1, pt2;int iret1, iret2;iret1 = pthread_create(&pt1, NULL, functionC, NULL);if (iret1) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret1);exit(EXIT_FAILURE);}iret2 = pthread_create(&pt2, NULL, functionC, NULL);if (iret2) {fprintf(stderr, "Error - pthread_create() return code: %d\n", iret2);exit(EXIT_FAILURE);}pthread_join(pt1, NULL);pthread_join(pt2, NULL);pthread_mutex_destroy(&mutex);exit(EXIT_SUCCESS);
}void* functionC(void *) {pthread_t pt = pthread_self();for (int i=0; i<3; i++) {pthread_mutex_lock(&mutex);cout <<pt <<" counter: " <<counter <<endl;counter ++;cout <<pt <<" counter: " <<counter <<endl;pthread_mutex_unlock(&mutex);sleep(1);}
}



################################################################


8.条件变量 - condition variables


条件变量(condition variables)提供了另外一种线程同步的方法。mutex通过控制线程访问数据来进行同步,而条件遍历(condition variables)则基于数据值来同步线程。

为了防止竟态条件(race condition),条件变量总是和mutex一起使用。


一个典型的条件变量使用过程如下:

Main线程:

1)声明和初始化需要同步的全局变量;

2)声明和初始化条件变量对象;

3)声明和初始化相联系的mutex;

4)创建线程A,B

A线程:

1)做一些事情,触发了某个条件(比如计数器达到了某个特定值);

2)mutex锁定,检查全局变量的值;

3)调用函数pthread_cond_wait,等待线程B的信号(signal)(注意:调用pthread_cond_wait函数后,会自动解锁mutex,所以线程B可以锁定它);

4)当接收到信号后,唤醒本线程,自动锁定mutex;

5)解锁mutex;

6)继续

B线程:

1)做一些事情;

2)锁定mutex;

3)使用线程A等待的全局变量;

4)检测全局变量值,如果它满足条件,则唤醒线程A;

5)解锁mutex;

6)继续


条件变量(condition variable)对象的类型名为pthread_mutex_t,并且必须在使用前被初始化。

有两种初始化方法:

a)静态初始化

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

b)动态初始化。使用函数pthread_cond_init

pthread_cond_init函数格式:

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
如果要设置条件变量参数,可以使用动态方法;参数attr也可以置为NULL


当条件变量(condition variable)不再需要时,使用函数pthread_cond_destroy销毁

pthread_cond_destroy函数格式:

int pthread_cond_destroy(pthread_cond_t *cond);


我们使用pthread_cond_wait和pthread_cond_signal函数来等待和唤醒条件变量(condition variable)

pthread_cond_wait函数格式:

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
函数功能:阻塞调用此函数的线程直到参数cond被唤醒(signal)。调用此函数前,应该锁定mutex,调用此函数后,mutex会自动解锁,当cond被唤醒后,重新锁定mutex。


pthread_cond_signal函数格式:

int pthread_cond_signal(pthread_cond_t *cond);
函数功能:唤醒另一个正在等待cond的线程。


如果想要同时唤醒多个线程,可以使用函数pthread_cond_broadcast:

pthread_cond_broadcast函数格式:

int pthread_cond_broadcast(pthread_cond_t *cond);


程序1:线程一输出1-3和8-10,线程二输出4-7

#include <iostream>
#include <pthread.h>
using namespace std;#define COUNT_DONE 10
#define COUNT_HALT1 3
#define COUNT_HALT2 6pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int count = 0;void* thread1(void*);
void* thread2(void*);int main(int argc, char* argv[]) {pthread_t pt1, pt2;int iret1, iret2;iret1 = pthread_create(&pt1, NULL, thread1, NULL);if (iret1) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret1);exit(EXIT_FAILURE);}iret2 = pthread_create(&pt2, NULL, thread2, NULL);if (iret2) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret2);exit(EXIT_FAILURE);}pthread_join(pt1, NULL);pthread_join(pt2, NULL);cout <<"Final count: " <<count <<endl;pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);exit(EXIT_SUCCESS);
}// Write numbers 1-3 and 8-10
void* thread1(void *) {while (true) {// Lock mutex and then wait for signal to release mutexpthread_mutex_lock(&mutex);// Wait while thread2 operates on count// mutex unlocked if condition variable in thread2 signaledpthread_cond_wait(&cond, &mutex);count ++;cout <<"thread1 count: " <<count <<endl;pthread_mutex_unlock(&mutex);if (count >= COUNT_DONE)pthread_exit(NULL);}
}// Write numbers 4-7
void* thread2(void*) {while (true) {pthread_mutex_lock(&mutex);if (count < COUNT_HALT1 || count > COUNT_HALT2) {pthread_cond_signal(&cond);} else {count ++;cout <<"thread2 count: " <<count <<endl;}pthread_mutex_unlock(&mutex);if (count >= COUNT_DONE)pthread_exit(NULL);}
}



程序2:同程序1同样的功能

#include <iostream>
#include <pthread.h>
using namespace std;#define COUNT_DONE 10
#define COUNT_HALT1 3
#define COUNT_HALT2 6pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int count = 0;void* thread1(void*);
void* thread2(void*);int main(int argc, char* argv[]) {pthread_t pt1, pt2;int iret1, iret2;iret1 = pthread_create(&pt1, NULL, thread1, NULL);if (iret1) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret1);exit(EXIT_FAILURE);}iret2 = pthread_create(&pt2, NULL, thread2, NULL);if (iret2) {fprintf(stderr,"Error - pthread_create() return code: %d\n",iret2);exit(EXIT_FAILURE);}pthread_join(pt1, NULL);pthread_join(pt2, NULL);cout <<"Final count: " <<count <<endl;pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);exit(EXIT_SUCCESS);
}// Write numbers 1-3 and 8-10
void* thread1(void *) {while (true) {// Lock mutex and then wait for signal to release mutexpthread_mutex_lock(&mutex);count ++;cout <<"thread1 count: " <<count <<endl;if (count >= COUNT_HALT1 || count <= COUNT_HALT2) {pthread_cond_wait(&cond, &mutex);}pthread_mutex_unlock(&mutex);if (count >= COUNT_DONE)pthread_exit(NULL);}
}// Write numbers 4-7
void* thread2(void*) {while (true) {pthread_mutex_lock(&mutex);if (count < COUNT_HALT1 || count > COUNT_HALT2) {pthread_cond_signal(&cond);} else {count ++;cout <<"thread2 count: " <<count <<endl;}pthread_mutex_unlock(&mutex);if (count >= COUNT_DONE)pthread_exit(NULL);}
}

程序3:实现同样的效果

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>#define NUM_THREADS  3
#define TCOUNT 5
#define COUNT_LIMIT 10int     count = 0;
int     thread_ids[3] = {0,1,2};
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;void *inc_count(void *t)
{int i;long my_id = (long)t;for (i=0; i<TCOUNT; i++) {pthread_mutex_lock(&count_mutex);count++;/*Check the value of count and signal waiting thread when condition isreached.  Note that this occurs while mutex is locked.*/if (count == COUNT_LIMIT) {pthread_cond_signal(&count_threshold_cv);printf("inc_count(): thread %ld, count = %d  Threshold reached.\n",my_id, count);}printf("inc_count(): thread %ld, count = %d, unlocking mutex\n",my_id, count);pthread_mutex_unlock(&count_mutex);/* Do some "work" so threads can alternate on mutex lock */sleep(1);}pthread_exit(NULL);
}void *watch_count(void *t)
{long my_id = (long)t;printf("Starting watch_count(): thread %ld\n", my_id);/*Lock mutex and wait for signal.  Note that the pthread_cond_waitroutine will automatically and atomically unlock mutex while it waits.Also, note that if COUNT_LIMIT is reached before this routine is run bythe waiting thread, the loop will be skipped to prevent pthread_cond_waitfrom never returning.*/pthread_mutex_lock(&count_mutex);while (count<COUNT_LIMIT) {pthread_cond_wait(&count_threshold_cv, &count_mutex);printf("watch_count(): thread %ld Condition signal received.\n", my_id);
//    count += 125;printf("watch_count(): thread %ld count now = %d.\n", my_id, count);}pthread_mutex_unlock(&count_mutex);pthread_exit(NULL);
}int main (int argc, char *argv[])
{int i, rc;long t1=1, t2=2, t3=3;pthread_t threads[3];pthread_attr_t attr;/* Initialize mutex and condition variable objects */pthread_mutex_init(&count_mutex, NULL);pthread_cond_init (&count_threshold_cv, NULL);/* For portability, explicitly create threads in a joinable state */pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);pthread_create(&threads[0], &attr, watch_count, (void *)t1);pthread_create(&threads[1], &attr, inc_count, (void *)t2);pthread_create(&threads[2], &attr, inc_count, (void *)t3);/* Wait for all threads to complete */for (i=0; i<NUM_THREADS; i++) {pthread_join(threads[i], NULL);}printf ("Main(): Waited on %d  threads. Done.\n", NUM_THREADS);/* Clean up and exit */pthread_attr_destroy(&attr);pthread_mutex_destroy(&count_mutex);pthread_cond_destroy(&count_threshold_cv);pthread_exit(NULL);}



#############################################################################################


上面这些陆续花了几个月时间学习,但感觉对于掌握pthread来说还缺点东西。


接下来学习目标:相关设计模式,线程池


这篇关于C/C++ pthead的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1081626

相关文章

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

详解C++中类的大小决定因数

《详解C++中类的大小决定因数》类的大小受多个因素影响,主要包括成员变量、对齐方式、继承关系、虚函数表等,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录1. 非静态数据成员示例:2. 数据对齐(Padding)示例:3. 虚函数(vtable 指针)示例:4. 继承普通继承虚继承5.

C++中std::distance使用方法示例

《C++中std::distance使用方法示例》std::distance是C++标准库中的一个函数,用于计算两个迭代器之间的距离,本文主要介绍了C++中std::distance使用方法示例,具... 目录语法使用方式解释示例输出:其他说明:总结std::distance&n编程bsp;是 C++ 标准