pthread_key_create()的使用
- 格式:docx
- 大小:16.40 KB
- 文档页数:4
linux pthread_create 参数Linux线程的创建是一个非常重要的话题,因为线程是一个应用程序中最基本的实体之一。
Linux中的线程是使用POSIX线程库(Pthread)实现的。
该库使得在Linux系统中使用线程非常方便。
本文将介绍Pthread库中的pthead_create()函数及其参数。
pthread_create() 函数原型:``` int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg); ```pthread_create 函数接受四个参数1.参数(thread): 指向pthread_t类型的指针,用来保存线程ID。
2.参数(attr): 指向 pthread_attr_t 类型的指针,用于设置线程的属性。
通常设置为NULL,使用默认属性。
3.参数(start_routine): 线程函数指针,该函数必须接受一个(void *)类型的参数,并返回一个(void *)类型的指针。
4.参数(arg): 传递给线程函数(start_routine)的参数。
线程创建完成后,它会执行调用 pthread_create() 函数的进程中的start_routine()函数,并将传递给pthread_create() 函数的参数(arg)传递给start_routine()函数。
以下是本函数传入的参数的详细说明。
1.参数(thread)参数thread是一个指针类型的变量,用来保存线程的ID。
在进程中创建一个线程时,线程的ID将存储在此指针中。
这个参数是必需的。
2.参数(attr)参数attr是一个指向pthread_attr_t类型结构体的指针。
pthread_attr_t是Linux系统中线程的属性类型,这个结构体包含了很多控制线程性质的变量,比如优先级,调度策略等等。
类成员函数作为pthread_create函数参数近⽇需要将线程池封装成C++类,类名为Threadpool。
在类的成员函数exec_task中调⽤pthread_create去启动线程执⾏例程thread_rounter。
编译之后报错如下:spfs_threadpool.cpp: In member function ‘int Threadpool::exec_task(task*)’:spfs_threadpool.cpp:174: error: argument of type ‘void* (Threadpool::)(void*)’ does not match ‘void* (*)(void*)’出现类型不匹配的问题。
因为pthread_create需要的参数类型为void* (*)(void*),⽽thread_rounter作为类的成员函数时其类型是void* (Threadpool::)(void*)的成员函数指针。
我们知道类的成员函数在经过编译器处理之后,会变成带有this指针参数的全局函数,所以类型注定是不会匹配的。
但是如果将thread_rounter声明为static类型,那么编译器会将static形式的函数,转换成不带this指针的全局函数,所以其类型可以与pthread_create需要的参数类型相匹配。
但是类的静态成员函数⽆法访问类的⾮静态成员,不过这可以通过传递this指针解决这个问题。
综上,我的这个问题可以这个样⼦解决。
出问题之前的代码:void *thread_rounter(void *)//线程执⾏函数{//直接访问类的成员}exec_task函数中调⽤:pthread_create(&tid,NULL,thread_rounter,NULL);//启动线程执⾏例程修复这个问题的代码:static void *thread_rounter(void *tmp)/线程执⾏函数{Threadpool *p=(Threadpool *)tmp;//通过p指针间接访问类的⾮静态成员}exec_task函数中调⽤:pthread_create(&tid,NULL,thread_rounter,(void *)this);//启动线程执⾏例程----------------------------------------------------------------------------------------------------------------------在⽹上搜索⼀下还有其他的解决⽅案,摘录如下,为了以⽰尊重标明⽂章来源,感谢原⽂作者。
c++ __thread 变量实现原理C++中的`__thread`变量是一种线程局部存储(Thread Local Storage,TLS)的实现方式。
它允许每个线程都有自己独立的变量副本,且该变量在不同线程之间是隔离的,互不干扰。
在本文中,我将详细介绍`__thread`变量的实现原理,包括TLS的概念,操作系统的支持,以及编译器的实现方式。
我们需要了解线程局部存储(TLS)的概念。
在多线程编程中,全局变量在多个线程之间是共享的,可能会出现数据竞争的问题。
为了解决这个问题,引入了TLS的概念,它能够为每个线程维护一个独立的变量副本。
这样,每个线程都可以独立地修改和访问它们自己的副本,从而避免了数据竞争的问题。
TLS的实现方式因操作系统的不同而有所区别。
在Linux上,使用`pthread_key_create`函数创建一个TLS键,并使用`pthread_setspecific`和`pthread_getspecific`函数来设置和获取TLS变量的值。
而在Windows上,使用`TlsAlloc`函数来分配一个TLS索引,并使用`TlsSetValue`和`TlsGetValue`函数来设置和获取TLS变量的值。
在C++中,`__thread`关键字可以用于声明一个TLS变量。
编译器会为每个线程生成一个独立的变量副本,并在每个线程中自动初始化和清理这个变量副本。
这样,我们就可以通过`__thread`关键字来实现线程之间的隔离,并避免了显式地调用TLS相关的函数。
接下来,让我们深入了解编译器是如何实现`__thread`变量的。
编译器会为每个TLS变量生成一个全局变量,这个全局变量是一个指针类型,指向当前线程的变量副本。
在每个线程的初始化代码中,编译器会生成一段特定的代码,用于初始化这个变量副本。
而在线程终止时,编译器也会生成相应的代码来清理这个变量副本。
在生成的全局变量中,编译器使用了一些特殊的机制来确保它是线程局部的。
一、实验目的1. 理解进程的概念,明确进程与程序的区别。
2. 掌握进程的创建、调度、同步、互斥和通信等基本操作。
3. 熟悉Linux操作系统下进程管理的常用命令和工具。
二、实验环境1. 操作系统:Linux2. 编程语言:C3. 开发工具:gcc三、实验内容1. 进程的创建与调度(1)使用fork()函数创建子进程```c#include <stdio.h>#include <unistd.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork failed");return 1;} else if (pid == 0) {// 子进程printf("This is child process.\n");return 0;} else {// 父进程printf("This is parent process.\n"); return 0;}}```(2)使用exec()函数替换子进程内容```c#include <stdio.h>#include <unistd.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork failed");return 1;} else if (pid == 0) {// 子进程execlp("ls", "ls", "-l", (char )NULL); perror("execlp failed");return 1;} else {// 父进程printf("This is parent process.\n");return 0;}}```2. 进程同步与互斥(1)使用信号量实现进程同步```c#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>int shared_resource = 0;void producer(void arg) {for (int i = 0; i < 5; i++) {pthread_mutex_lock(&mutex);shared_resource++;printf("Producer: %d\n", shared_resource); pthread_mutex_unlock(&mutex);sleep(1);}return NULL;}void consumer(void arg) {for (int i = 0; i < 5; i++) {pthread_mutex_lock(&mutex);shared_resource--;printf("Consumer: %d\n", shared_resource); pthread_mutex_unlock(&mutex);sleep(1);}return NULL;}int main() {pthread_mutex_t mutex;pthread_t prod, cons;pthread_mutex_init(&mutex, NULL);pthread_create(&prod, NULL, producer, NULL); pthread_create(&cons, NULL, consumer, NULL); pthread_join(prod, NULL);pthread_join(cons, NULL);pthread_mutex_destroy(&mutex);return 0;}```(2)使用互斥锁实现进程互斥#include <stdio.h>#include <stdlib.h>#include <pthread.h>int shared_resource = 0;pthread_mutex_t lock;void producer(void arg) {for (int i = 0; i < 5; i++) {pthread_mutex_lock(&lock);shared_resource++;printf("Producer: %d\n", shared_resource); pthread_mutex_unlock(&lock);sleep(1);}return NULL;}void consumer(void arg) {for (int i = 0; i < 5; i++) {pthread_mutex_lock(&lock);shared_resource--;printf("Consumer: %d\n", shared_resource); pthread_mutex_unlock(&lock);sleep(1);return NULL;}int main() {pthread_t prod, cons;pthread_mutex_init(&lock, NULL);pthread_create(&prod, NULL, producer, NULL); pthread_create(&cons, NULL, consumer, NULL); pthread_join(prod, NULL);pthread_join(cons, NULL);pthread_mutex_destroy(&lock);return 0;}```3. 进程通信(1)使用管道实现进程通信```c#include <stdio.h>#include <stdlib.h>#include <unistd.h>int main() {int pipefd[2];pid_t cpid;if (pipe(pipefd) == -1) {perror("pipe");exit(EXIT_FAILURE);}cpid = fork();if (cpid == -1) {perror("fork");exit(EXIT_FAILURE);}if (cpid == 0) { // 子进程close(pipefd[1]); // 关闭写端dup2(pipefd[0], STDIN_FILENO); // 将读端复制到标准输入 char args[] = {"./consumer", NULL};execvp(args[0], args);perror("execvp");exit(EXIT_FAILURE);} else { // 父进程close(pipefd[0]); // 关闭读端char buffer[1024];write(pipefd[1], "Hello, consumer!\n", 18);read(pipefd[0], buffer, 1024);printf("Parent: %s", buffer);close(pipefd[1]);wait(NULL);}return 0;}```(2)使用共享内存实现进程通信```c#include <stdio.h>#include <stdlib.h>#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>#define SHM_SIZE 1024int main() {key_t key = 1234;int shmid;char shm, s;shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT); if (shmid == -1) {perror("shmget");exit(EXIT_FAILURE);}shm = shmat(shmid, (void )0, 0);if (shm == (char )(-1)) {perror("shmat");exit(EXIT_FAILURE);}s = shm;while (s != '\0') {putchar(s);s++;}putchar('\n');shmdt(shm);shmctl(shmid, IPC_RMID, NULL);return 0;}```四、实验总结通过本次实验,我们深入了解了Linux操作系统下进程管理的基本操作,包括进程的创建、调度、同步、互斥和通信等。
linux pthread 用法Linux pthread(POSIX线程)是一种多线程库,它提供了在Linux系统上创建和管理线程的API。
使用pthread库,可以编写多线程程序,实现并发执行和资源共享。
下面是一些常用的pthread函数和用法:1.pthread_create():用于创建一个新的线程。
它接受一个指向线程属性的指针,一个指向线程函数的指针,以及传递给线程函数的参数。
函数原型为:intpthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg);2.pthread_join():用于等待一个线程的结束。
它接受一个指向线程标识符的指针,以及一个指向用于存储线程返回值的指针的指针。
函数原型为:intpthread_join(pthread_t thread, void **retval);3.pthread_self():用于获取当前线程的标识符。
函数原型为:pthread_tpthread_self(void);4.pthread_detach():用于将一个线程从系统中分离出去。
这通常用于在后台运行的任务,不需要手动等待它们完成。
函数原型为:int pthread_detach(pthread_t thread);5.pthread_equal():用于比较两个线程标识符是否相等。
函数原型为:intpthread_equal(pthread_t thread1, pthread_t thread2);6.pthread_mutex_init():用于初始化一个互斥锁。
函数原型为:intpthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 7.pthread_mutex_lock():用于获取一个互斥锁。
pthread_create 原理-概述说明以及解释1.引言1.1 概述pthread_create是一个用于创建线程的函数,它的作用是创建一个新的线程并执行指定的函数。
在多线程编程中,pthread_create函数是非常重要的一个函数,它可以实现并发执行,提高程序的性能和效率。
通过pthread_create函数,我们可以轻松地创建多个线程来执行不同的任务,从而实现程序的并发执行。
在传统的单线程程序中,所有的任务都是顺序执行的,当遇到阻塞或耗时任务时,整个程序会被阻塞,影响程序的执行效率。
而通过多线程编程,可以将这些任务分配给不同的线程来执行,从而提高程序的并发性和响应速度。
本文将介绍pthread_create函数的原理、用法和应用场景,帮助读者更好地了解和掌握这个重要的多线程操作函数。
通过深入理解pthread_create函数,读者可以更好地利用多线程编程提高程序性能,并更好地应对实际软件开发中的并发需求。
1.2 文章结构:本文将围绕pthread_create函数展开讨论,主要分为三个部分:引言、正文和结论。
在引言部分,将对pthread_create函数进行概述,介绍文章的结构以及明确文章的目的。
在正文部分,将详细介绍pthread_create函数的功能和用法,分析其原理并探讨其应用场景。
在结论部分,将总结pthread_create函数的重要性,提出使用该函数时需要注意的事项,并展望其未来的发展前景。
结构部分的内容1.3 目的本文旨在深入探讨pthread_create函数的原理和应用,帮助读者更好地理解多线程编程的基本原理和实践方法。
通过对pthread_create函数的介绍和分析,读者可以了解到如何使用该函数创建新的线程,并且掌握线程管理的关键技巧。
同时,本文还将探讨pthread_create函数的应用场景,帮助读者更好地应用多线程技术解决实际问题。
通过阅读本文,读者可以深入了解pthread_create函数的实现原理,为提高程序的并发性能和可维护性提供参考。
#include<pthread.h>1、创建int pthread_create(pthread_t*tid,const pthread_attr_t*attr,void*(*func)(void*),void*arg);attr:线程属性包括:优先级、初始栈大小,是否应该成为一个守护线程。
缺省设置,NULL后面是线程要执行的函数和参数成功返回02、等待一个给定线程终止int pthread_join(pthread_t tid,void**status);statues返回等待线程的返回值3、得到自身的pidpthread_t pthread_self(void);4、pthread_detach函数int pthread_detach(pthread_t pid);把指定的线程转变为脱离状态一个线程或者是可汇合的(joinable,缺省值),或者是脱离的(detached)。
当一个可汇合的线程终止时,它的线程ID和退出状态将留到另一个线程对它调用pthread_join。
脱离线程却象守护进程:当它们终止的时,所有相关资源都被释放,我们不能等待它们终止。
如果一个线程需要知道另一个线程什么时候终止,那就最好好吃第二个线程的可汇合状态。
本函数通常由想让自己脱离的线程调用,如下语句pthread_detach(pthread_self());5、终止一个线程void pthread_exit(void*statue);指针sttus不能指向局部于调用对象,因为线程终止时这样的对象也消失1.线程属性设置我们用pthread_create函数创建一个线程,在这个线程中,我们使用默认参数,即将该函数的第二个参数设为NULL。
的确,对大多数程序来说,使用默认属性就够了,但我们还是有必要来了解一下线程的有关属性。
属性结构为pthread_attr_t,它同样在头文件pthread.h中定义,属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。
线程私有数据(TSD)线程中特有的线程存储, Thread Specific Data 。
线程存储有什么⽤了?他是什么意思了?⼤家都知道,在多线程程序中,所有线程共享程序中的变量。
现在有⼀全局变量,所有线程都可以使⽤它,改变它的值。
⽽如果每个线程希望能单独拥有它,那么就需要使⽤线程存储了。
表⾯上看起来这是⼀个全局变量,所有线程都可以使⽤它,⽽它的值在每⼀个线程中⼜是单独存储的。
这就是线程存储的意义。
下⾯说⼀下线程存储的具体⽤法。
l 创建⼀个类型为 pthread_key_t 类型的变量。
l 调⽤ pthread_key_create() 来创建该变量。
该函数有两个参数,第⼀个参数就是上⾯声明的 pthread_key_t 变量,第⼆个参数是⼀个清理函数,⽤来在线程释放该线程存储的时候被调⽤。
该函数指针可以设成 NULL ,这样系统将调⽤默认的清理函数。
l 当线程中需要存储特殊值的时候,可以调⽤ pthread_setspcific() 。
该函数有两个参数,第⼀个为前⾯声明的 pthread_key_t 变量,第⼆个为 void* 变量,这样你可以存储任何类型的值。
l 如果需要取出所存储的值,调⽤ pthread_getspecific() 。
该函数的参数为前⾯提到的 pthread_key_t 变量,该函数返回 void * 类型的值。
下⾯是前⾯提到的函数的原型:int pthread_setspecific(pthread_key_t key, const void *value);void *pthread_getspecific(pthread_key_t key);int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));下⾯是⼀个如何使⽤线程存储的例⼦:#include <stdio.h>#include <pthread.h>pthread_key_t key;void echomsg(int t){printf("destructor excuted in thread %d,param=%d\n",pthread_self(),t);}void * child1(void *arg){int tid=pthread_self();printf("thread %d enter\n",tid);pthread_setspecific(key,(void *)tid);sleep(2);printf("thread %d returns %d\n",tid,pthread_getspecific(key));sleep(5);}void * child2(void *arg){int tid=pthread_self();printf("thread %d enter\n",tid);pthread_setspecific(key,(void *)tid);sleep(1);printf("thread %d returns %d\n",tid,pthread_getspecific(key));sleep(5);}int main(void){int tid1,tid2;printf("hello\n");pthread_key_create(&key,echomsg);pthread_create(&tid1,NULL,child1,NULL);pthread_create(&tid2,NULL,child2,NULL);sleep(10);pthread_key_delete(key);printf("main thread exit\n");return0;}线程的本质:其实在Linux 中,新建的线程并不是在原先的进程中,⽽是系统通过⼀个系统调⽤clone() 。
pthreadcreate参数
pthread_create函数用于创建一个新的线程,并将其加入到当前进程中。
该函数的参数包括四个,分别为线程标识符、线程属性、线程函数和函数参数。
下面分别介绍这些参数的含义:
1. 线程标识符:是指向一个pthread_t类型的变量的指针,用于存储创建的线程的ID号。
当新线程创建成功后,该变量将被填充上该线程的ID号。
2. 线程属性:是指向一个pthread_attr_t类型的变量的指针,用于设置线程的属性。
如果不需要设置属性,则可以将该参数设置为NULL。
3. 线程函数:是指一个函数指针,该函数指针指向一个线程函数,用于定义新线程的行为。
4. 函数参数:是指传递给线程函数的参数。
如果不需要传递参数,则可以将该参数设置为NULL。
在使用pthread_create函数时,需要注意以下几点:
1. 线程标识符必须是一个有效的指针,且该指针不应该被释放,直到线程结束。
2. 如果没有特殊要求,可以将线程属性设置为NULL。
3. 线程函数必须是一个指向函数的指针,并且该函数必须具有以下形式:void *function(void *arg),其中arg为指向函数参数的指针。
4. 函数参数必须被声明为void类型的指针,即void *类型。
5. 线程函数的返回值必须为void *类型,即指向任意类型的指针。
6. 当线程函数执行完毕后,应该调用pthread_exit函数来终止该线程。
7. 在主线程中调用pthread_join函数可以等待指定的线程结束。
pthread_key_t和pthread_key_create()的使用
下面说一下线程中特有的线程存储, Thread Specific Data 。
线程存储有什么用了?他是什么意思了?大家都知道,在多线程程序中,所有线程共享程序中的变量。
现在有一全局变量,所有线程都可以使用它,改变它的值。
而如果每个线程希望能单独拥有它,那么就需要使用线程存储了。
表面上看起来这是一个全局变量,所有线程都可以使用它,而它的值在每一个线程中又是单独存储的。
这就是线程存储的意义。
下面说一下线程存储的具体用法。
l 创建一个类型为 pthread_key_t 类型的变量。
l 调用 pthread_key_create() 来创建该变量。
该函数有两个参数,第一个参数就是上面声明的 pthread_key_t 变量,第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。
该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。
l 当线程中需要存储特殊值的时候,可以调用 pthread_setspcific() 。
该函数有两个参数,第一个为前面声明的 pthread_key_t 变量,第二个为 void* 变量,这样你可以存储任何类型的值。
l 如果需要取出所存储的值,调用 pthread_getspecific() 。
该函数的参数为前面提到的 pthread_key_t 变量,该函数返回 void * 类型的值。
下面是前面提到的函数的原型:
intpthread_setspecific(pthread_key_t key, const void *value);
void *pthread_getspecific(pthread_key_t key);
intpthread_key_create(pthread_key_t *key, void (*destructor)(void*));
下面是一个如何使用线程存储的例子:
#include <malloc.h>
#include <pthread.h>
#include <stdio.h>
/* The key used to associate a log file pointer with each thread. */
staticpthread_key_tthread_log_key;
/* Write MESSAGE to the log file for the current thread. */
voidwrite_to_thread_log (const char* message)
{
FILE* thread_log = (FILE*) pthread_getspecific (thread_log_key);
fprintf (thread_log, “%s\n”, message);
}
/* Close the log file pointer THREAD_LOG. */
voidclose_thread_log (void* thread_log)
{
fclose ((FILE*) thread_log);
}
void* thread_function (void* args)
{
charthread_log_filename[20];
FILE* thread_log;
/* Generate the filename for this thread’s log file. */
sprintf (thread_log_filename, “thread%d.log”, (int) pthread_self());
/* Open the log file. */
thread_log = fopen (thread_log_filename, “w”);
/* Store the file pointer in thread-specific data under thread_log_key. */
pthread_setspecific (thread_log_key, thread_log);
write_to_thread_log (“Thread starting.”);
/* Do work here... */
return NULL;
}
int main ()
{
int i;
pthread_t threads[5];
/* Create a key to associate thread log file pointers in
thread-specific data. Use close_thread_log to clean up the file pointers. */
pthread_key_create (&thread_log_key, close_thread_log);
/* Create threads to do the work. */
for (i = 0; i < 5; ++i)
pthread_create (&(threads[i]), NULL, thread_function, NULL);
/* Wait for all threads to finish. */
for (i = 0; i < 5; ++i)
pthread_join (threads[i], NULL);
return 0;
}
最后说一下线程的本质。
其实在Linux 中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone() 。
该系统copy 了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。
不过这个copy 过程和fork 不一样。
copy 后的进程和原先的进程共享了所有的变量,运行环境。
这样,原先进程中的变量变动在copy 后的进程中便能体现出来。