Linux中的POSIX线程(pthread库)

目录

1.概述

2.线程函数

2.1pthread_create创建线程

2.2pthread_join线程阻塞

2.3pthread_exit线程退出

2.4pthread_self获取线程标识

2.5pthread_cancel取消线程

2.6pthread_detach线程分离

2.7pthread_equal线程比较

3.线程属性

3.1属性结构

3.2init初始化与destroy销毁

3.3线程的优先级Sched

3.4线程的作用域Scope

3.5线程的分离状态Detachstate


1.概述

线程是操作系统能够并发执行的一个基本单元,它是进程的一个执行实例。线程与进程的区别在于,线程共享进程的地址空间,而进程之间地址空间是相互独立的。线程的创建和撤销比进程要简单,线程之间的切换也比进程之间的切换要快。

C/C++中提供了pthread库来支持线程的创建和管理。pthread库是Linux系统下的线程库,在Windows系统下不能直接使用。

pthread库需要声明头文件:

#include <pthread.h>

2.线程函数

2.1pthread_create创建线程

pthread库提供了以下函数来创建线程:

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

该函数用于创建一个新线程,参数thread指向新线程的标识符,attr指向线程属性,start_routine指向线程执行函数,arg指向线程执行函数的参数。

pthread_create函数如果创建线程成功,则返回值是0;否则,返回错误码。

pthread_create函数的错误码如下:

EAGAIN:系统中没有可用的线程。

EINVAL:参数attr无效。

EPERM:当前进程没有创建线程的权限。

ENOMEM:内存不足。

线程执行函数的声明如下:

void *thread_function(void *arg);

线程执行函数可以是任何C函数,但是该函数不能有返回值,并且该函数不能调用exit函数

2.2pthread_join线程阻塞

创建线程后,可以使用pthread_join函数等待一个线程结束,并获取该线程的退出状态。(线程阻塞函数,调用该函数则等到线程结束才继续运行)该函数的声明如下:

int pthread_join(pthread_t thread, void **retval);

参数thread指向要等待的线程,retval指向一个指针,该指针将存储线程的退出状态。

如果成功,则pthread_join函数的返回值是0;否则,返回错误码。

 pthread_join函数的错误码如下:

ESRCH:参数thread指向的线程不存在。

EDEADLK:当前线程尝试等待自己。

EINVAL:参数thread指向的线程已经结束。

EINTR:等待线程被中断。

下面是一个简单的线程创建和等待示例:

#include <stdio.h>
#include <pthread.h>

void *thread_function(void *arg) {
    printf("This is a thread.\n");
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
    printf("Thread finished.\n");
    return 0;
}

运行该程序,输出如下:

This is a thread.
Thread finished.

2.3pthread_exit线程退出

pthread_exit函数用于在线程中显式地退出线程,并返回一个指针值作为线程的退出状态。该函数的声明如下:

  void pthread_exit(void *retval);

参数retval是一个指针,用于传递线程的退出状态。线程的退出状态可以是任何类型的指针。 pthread_exit函数没有返回值。 使用pthread_exit函数可以在任何地方显式地终止线程的执行,并将退出状态传递给等待该线程的其他线程。 下面是一个使用pthread_exit函数的示例:

#include <stdio.h>
#include <pthread.h>

void *thread_function(void *arg) {
    printf("Thread is exiting.\n");
    int *result = malloc(sizeof(int));
    *result = 42;
    pthread_exit(result);
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    
    void *exit_status;
    pthread_join(thread, &exit_status);
    
    int *result = (int *)exit_status;
    printf("Thread exited with status: %d\n", *result);
    free(result);
    
    return 0;
}

运行该程序,输出如下:

Thread is exiting.
Thread exited with status: 42

在上面的示例中,线程函数thread_function在退出之前动态分配了一个int类型的内存,并将其作为退出状态传递给pthread_exit函数。在主线程中,通过pthread_join函数获取线程的退出状态,并进行相应的处理。

2.4pthread_self获取线程标识

pthread_self函数用于获取当前线程的线程标识符。该函数的声明如下:

pthread_t pthread_self(void);

pthread_self函数的返回值是当前线程的线程标识符。 pthread_self函数的使用示例如下:

#include <stdio.h>
#include <pthread.h>

void *thread_function(void *arg) {
    printf("This is a thread.\n");
    pthread_t thread_id = pthread_self();
    printf("My thread id is %lu.\n", thread_id);
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
    return 0;
}

运行该程序,输出如下:

This is a thread.
My thread id is 140737440306016.

在上面这个例子中,创建了一个线程,并在线程中调用pthread_self函数来获取线程标识符。然后,在主线程中调用pthread_join函数来等待线程结束。当线程结束时,会在主线程中打印线程标识符。

2.5pthread_cancel取消线程

pthread_cancel函数用于向指定的线程发送取消请求,请求线程退出。被取消的线程需要在适当的地方检查取消请求,并执行相应的清理操作。 pthread_cancel函数的声明如下: 

  int pthread_cancel(pthread_t thread);

参数thread指向要取消的线程。

如果成功,则pthread_cancel函数的返回值是0;否则,返回错误码。 pthread_cancel函数的错误码如下:

ESRCH:参数thread指向的线程不存在

EINVAL:参数thread指向的线程已经结束

pthread_cancel函数的使用示例如下:

#include <stdio.h>
#include <pthread.h>

void *thread_function(void *arg) {
    printf("This is a thread.\n");
    while (1) {
        // 检查是否被取消
        if (pthread_cancel(pthread_self())) {
            // 线程被取消,执行清理操作
            break;
        }
    }
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    // 等待线程结束
    pthread_join(thread, NULL);
    return 0;
}

运行该程序,输出如下:

This is a thread.

在上面这个例子中,创建了一个线程,并在主线程中调用pthread_cancel函数来取消该线程。当线程被取消时,它会执行清理操作,然后退出。

2.6pthread_detach线程分离

pthread_detach函数用于将指定的线程标记为可分离的,这样在线程终止时,系统会自动回收线程的资源,而不需要调用pthread_join来等待线程结束。 pthread_detach函数的声明如下:

int pthread_detach(pthread_t thread);

参数thread指向要分离的线程。

如果成功,则pthread_detach函数的返回值是0;否则,返回错误码。

pthread_detach函数的错误码如下: 

ESRCH:参数thread指向的线程不存在。

EINVAL:参数thread指向的线程已经结束。

pthread_detach函数的使用示例如下:

#include <stdio.h>
#include <pthread.h>

void *thread_function(void *arg) {
    printf("This is a thread.\n");
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_detach(thread);
    return 0;
}

运行该程序,输出如下:

This is a thread.

在上面这个例子中,创建了一个线程,并调用pthread_detach函数将该线程标记为可分离的。当线程结束时,系统会自动回收线程的资源,而不需要调用pthread_join来等待线程结束。

2.7pthread_equal线程比较

pthread_equal函数用于比较两个线程标识符是否相等。该函数的声明如下:

int pthread_equal(pthread_t t1, pthread_t t2);

参数t1和t2是两个线程标识符。

如果两个线程标识符相等,pthread_equal函数的返回值是0;否则,返回非0值。

pthread_equal函数的使用示例如下:

#include <stdio.h>
#include <pthread.h>

void *thread_function(void *arg) {
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, thread_function, NULL);
    pthread_create(&thread2, NULL, thread_function, NULL);

    if (pthread_equal(thread1, thread2)) {
        printf("The two threads are equal.\n");
    } else {
        printf("The two threads are not equal.\n");
    }

    return 0;
}

运行该程序,输出如下:

The two threads are not equal.

在上面这个例子中,创建了两个线程,并使用pthread_equal函数比较它们是否相等。结果显示,两个线程是不相等的。

3.线程属性

3.1属性结构

线程的属性结构为 pthread_attr_t,同样在<pthread.h>头文件中定义,如下所示:

typedef struct pthread_attr_s
{
    int                       detachstate;     //线程的分离状态
    int                       schedpolicy;     //线程优先级
    struct sched_param        schedparam;      //线程调度参数
    int                       inheritsched;    //线程的继承性
    int                       scope;           //线程的作用域
    size_t                    guardsize;       //线程栈末尾的警戒缓冲区尺寸
    int                       stackaddr_set;   //线程的运行栈
    void *                    stackaddr;       //线程栈的地址
    size_t                    stacksize;       //线程栈的大小
}pthread_attr_t;

属性对象主要包括线程的分离状态、调度优先级、运行栈地址、运行栈大小、优先级。

但是线程的属性值不能直接设置,须使用相关函数进行操作。

3.2init初始化与destroy销毁

线程属性的初始化函数为pthread_attr_init(&attribute), 且这个函数必须在 pthread_create()函数之前调用。

线程属性的销毁函数为pthread_attr_destroy(&attribute)。

运用示例如下所示:

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

void *thread_function(void *arg) {
    printf("Thread is running\n");
    sleep(2);
    printf("Thread is exiting\n");
    pthread_exit(NULL);
}

int main() {
    pthread_t thread;
    pthread_attr_t attr;

    // 初始化线程属性
    pthread_attr_init(&attr);

    // 创建线程
    pthread_create(&thread, &attr, thread_function, NULL);

    // 等待线程
    pthread_join(thread, NULL);

    // 销毁线程属性
    pthread_attr_destroy(&attr);

    return 0;
}

3.3线程的优先级Sched

线程的优先级是经常设置的属性,由两个函数进行控制: pthread_attr_getschedparam()函数获得线程的优先级设置,函数pthread_attr_setschedparam设置线程的优先级。

int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) 

 此函数从指定的线程属性对象 attr 中检索调度参数(如优先级),并将其存储在指向sched_param 结构的 param 指针中。
- attr 参数是指向要检索调度参数的线程属性对象的指针。
- param 参数是指向 sched_param 结构的指针,用于存储调度参数。
- 函数在成功时返回 0,如果出现错误则返回错误号。

int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) 

- 此函数根据 param 指向的 sched_param 结构中提供的值,在指定的线程属性对象 attr 中设置调度参数(如优先级)。
- attr 参数是指向要设置调度参数的线程属性对象的指针。
- param 参数是指向包含要设置的调度参数的 sched_param 结构的指针。
- 函数在成功时返回 0,如果出现错误则返回错误号。

线程的优先级存放在结构sched_param中,因为结构体sched_param在头文件<sched.h>中,所以要加入头文件<sched.h>。

struct sched_param {
    int sched_priority;  // Priority of the thread
    // Other scheduling parameters may be included here depending on the system
};

线程优先级的操作方式是先将优先级取出来,然后对需要设置的参数修改后再写回去,这是对复杂结构进行设置的通用办法,防止因为设置不当造成不可预料的麻烦。例如设置优先级的代码如下:

#include <stdio.h>
#include <pthread.h>
#include <sched.h>
pthread_attr_t attr;  
struct sched_param sch;
pthread_t pt;
pthread_attr_init(&attr);  /*初始化属性设置*/
pthread_attr_getschedparam(&attr, &sch);  /*获得当前的线程属性*/
sch.sched_priority = 256;  /*设置线程优先级为256*/
pthread_attr_setschedparam(&attr, &sch);  /*设置线程优先级*/
pthread_create(&pt, &attr, (void*)start_routine, &run);  /*建立线程,属性为上述设置*/

3.4线程的作用域Scope

pthread_attr_setscope() 函数是 POSIX 线程库中用于设置线程属性对象中线程作用域的函数。线程的作用域决定了线程与其他线程之间的共享资源范围。该函数的函数原型如下:

int pthread_attr_setscope(pthread_attr_t *attr, int scope);

- attr 参数是指向要设置作用域的线程属性对象的指针。
- scope 参数指定线程的作用域,可以是 PTHREAD_SCOPE_SYSTEM 或PTHREAD_SCOPE_PROCESS 。
- PTHREAD_SCOPE_SYSTEM 表示线程与整个系统共享资源,是线程的默认作用域。
- PTHREAD_SCOPE_PROCESS 表示线程与其所在的进程共享资源,线程的资源范围限定在进程内。

pthread_attr_setscope 函数用于设置线程的作用域,从而影响线程的资源共享范围。通过设置不同的作用域,您可以控制线程与其他线程之间的资源隔离程度。在多线程编程中,根据需要选择合适的线程作用域对于确保线程间资源的合理分配和管理是非常重要的。

3.5线程的分离状态Detachstate

pthread_attr_setdetachstate 函数是 POSIX 线程库中用于设置线程属性对象中线程分离状态的函数。线程的分离状态决定了线程结束时是否需要等待其他线程对其进行清理。该函数的函数原型如下:

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

- attr 参数是指向要设置分离状态的线程属性对象的指针。
- detachstate 参数指定线程的分离状态,可以是 PTHREAD_CREATE_JOINABLE 或 PTHREAD_CREATE_DETACHED 。
- PTHREAD_CREATE_JOINABLE 表示线程是可连接的(非分离),主线程可以调用 pthread_join 来等待该线程的结束,并获取其返回值。
- PTHREAD_CREATE_DETACHED 表示线程是分离的,线程结束后资源会被自动释放,无法被其他线程等待或获取返回值。

在之前的例子中,线程建立的时候没有设置属性,默认终止方法为非分离状态。在这种情况下,需要等待创建线程的结束。只有当pthread_join()函数返回回时,线程才算终止,并且释放线程创建的时候系统分配的资源。
分离线程不用其他线程等待,当前线程运行结束后线程就结束了,并且马上释放资源。当将一个线程设置为分离线程时,如果线程的运行非常快,可能在pthread_create()函数返回之前就终止了。由于一个线程在终止以后可以将线程号和系统资源移交给其他的线程使用,此时再使用函数pthread_create(获得的线程号进行操作会发生错误。

线程的分离方式可以根据需要,选择适当的分离状态。

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用 POSIX 信号量实现多个线程同时访问共享资源的例子: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #define MAX_THREADS 10 #define NUM_ITERATIONS 5 sem_t sem; // 定义信号量 int shared_resource = 0; // 共享资源 void *thread_func(void *arg) { int i; int id = *((int *) arg); for (i = 0; i < NUM_ITERATIONS; i++) { sem_wait(&sem); // 等待信号量 shared_resource++; // 访问共享资源 printf("Thread %d updated shared_resource to %d\n", id, shared_resource); sem_post(&sem); // 释放信号量 } pthread_exit(NULL); } int main(int argc, char *argv[]) { int i; pthread_t threads[MAX_THREADS]; int thread_ids[MAX_THREADS]; sem_init(&sem, 0, 1); // 初始化信号量 for (i = 0; i < MAX_THREADS; i++) { thread_ids[i] = i; pthread_create(&threads[i], NULL, thread_func, (void *) &thread_ids[i]); } for (i = 0; i < MAX_THREADS; i++) { pthread_join(threads[i], NULL); } sem_destroy(&sem); // 销毁信号量 return 0; } ``` 在上面的例子,我们创建了多个线程,每个线程都会访问共享资源 shared_resource。为了避免多个线程同时访问该资源,我们使用了一个信号量 sem,只有获得了该信号量的线程才能访问 shared_resource。在每个线程访问 shared_resource 之前,它会调用 sem_wait() 等待信号量,表示它要访问 shared_resource 了。在访问完 shared_resource 后,线程会调用 sem_post() 释放信号量,表示它已经访问完了 shared_resource,其他线程可以开始访问了。 需要注意的是,在上面的例子,我们使用了互斥信号量,即 sem 的初始值为 1。这意味着同一时刻只有一个线程可以访问 shared_resource。如果想要多个线程同时访问 shared_resource,可以使用非互斥信号量,即将 sem 的初始值设置为大于 1 的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值