本文分享自华为云社区《确保并发执行的安全性:探索多线程和锁机制以构建可靠的程序》,作者:Lion Long。
在当今计算机系统中,多线程编程已成为常见的需求,然而,同时也带来了并发执行的挑战。为了避免数据竞争和其他并发问题,正确使用适当的锁机制是至关重要的。通过阅读本文,读者将了解到多线程和锁机制在并发编程中的重要性,以及如何避免常见的并发问题,确保程序的安全性和可靠性。通过实际案例和代码示例来说明如何正确地使用多线程和锁机制来构建可靠的程序。
一、多线程的使用
1.1、线程的创建
函数原型:
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); // Compile and link with -pthread.
描述:
pthread_create()函数在调用进程中启动一个新线程。新线程通过调用start_routine()开始执行;arg作为start_routine()的唯一参数传递。
新线程以以下方式之一终止:
(1)它调用pthread_exit(),指定一个退出状态值,该值可用于调用pthrread_join()的同一进程中的另一个线程,即pthrread_join()可以接收pthread_exit()返回的值。
(2)它从start_routine()返回。这相当于使用return语句中提供的值调用pthread_exit()。
(3)它被pthread_cancel()取消。
(4)进程中的任何线程都调用exit(),或者主线程执行main()的返回。这将导致进程中所有线程的终止。
参数介绍:
| 参数 | 含义 |
|---|---|
| attr | attr参数指向pthread_attr_t结构,其内容在线程创建时用于确定新线程的属性;使用pthread_attr_init()和相关函数初始化该结构。如果attr为空,则使用默认属性创建线程。 |
| thread | 在返回之前,成功调用pthread_create()将新线程的ID存储在thread指向的缓冲区中;此标识符用于在后续调用其他pthreads函数时引用线程。 |
| start_routine | 线程入口函数 |
| arg | 线程入口函数的参数 |
返回值:
成功时,返回0;出错时,它返回一个错误号,并且*thread的内容未定义。
错误号:
| 错误号 | 含义 |
|---|---|
| EAGAIN | 资源不足,无法创建另一个线程。 |
| AGAIN A | 遇到系统对线程数量施加的限制。可能触发此错误的限制有很多:已达到RLIMIT_NPROC软资源限制【通过setrlimit()设置】,该限制限制了真实用户ID的进程和线程数;已达到内核对进程和线程数的系统范围限制,即/proc/sys/kernel/threads max【请参阅proc()】;或者达到最大pid数/proc/sys/kernel/pid_max【见proc()】。 |
| EINVAL | 属性中的设置无效。 |
| EPERM | 没有设置attr中指定的调度策略和参数的权限。 |
其他:
新线程继承创建线程的信号掩码【pthread_sigmask()】的副本。新线程的挂起信号集为空【sigpending()】。新线程不继承创建线程的备用信号堆栈【sigaltstack()】。
新线程的CPU时间时钟的初始值为0【参见pthread_getcpuclockid()】。
示例代码:
#include <pthread.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <ctype.h> #define handle_error_en(en, msg) do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) #define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) struct thread_info { /* Used as argument to thread_start() */ pthread_t thread_id; /* ID returned by pthread_create() */ int thread_num; /* Application-defined thread # */ char *argv_string; /* From command-line argument */ }; /* Thread start function: display address near top of our stack, and return upper-cased copy of argv_string */ static void * thread_start(void *arg) { struct thread_info *tinfo = arg; char *uargv, *p; printf("Thread %d: top of stack near %p; argv_string=%sn", tinfo->thread_num, &p, tinfo->argv_string); uargv = strdup(tinfo->argv_string); if (uargv == NULL) handle_error("strdup"); for (p = uargv; *p != '