概述
这一节先讲一下线程的基本应用作为开篇。
A single flow of control within a process. Each thread has its own thread ID, scheduling priority and policy, errno value, floating point environment, thread-specific key/value bindings, and the required system resources to support a flow of control. Anything whose address may be determined by a thread, including but not limited to static variables, storage obtained via malloc(), directly addressable storage obtained through implementation-defined functions, and automatic variables, are accessible to all threads in the same process. (POSIX.1原文)。
POSIX.1认为在进程当中,单个控制流被称为线程(thread)。每个线程都有其唯一的线程ID、调度优先级和策略、errno值、浮点环境、线程特定的键/值绑定,以及支持控制流所需的系统资源。 在一个进程中,任何可以通过线程确定其地址的实体,包括但不限于静态变量、通过malloc()函数获得的存储、通过实现定义函数获得的可直接寻址的存储,以及自动变量,都是该进程中所有线程可访问的。
定义就这样了,那么线程它有什么优势呢?如下:
- 资源共享:同一进程中的多个线程可以共享该进程的资源,如内存空间、打开的文件、数据库连接等。这种共享机制减少了资源创建和销毁的开销,提高了资源利用率。
- 独立执行:每个线程都有自己的执行上下文,包括程序计数器、栈指针和寄存器状态等。这使得线程能够独立地执行代码,而不受其他线程的影响。
- 并发执行:多线程程序可以在多核或多CPU的计算机上并发执行,充分利用硬件资源。即使在单CPU的计算机上,线程也可以通过时间片轮转的方式实现伪并发执行,从而提高程序的吞吐量和响应性。
- 通信与同步:线程之间可以通过共享内存进行通信,也可以使用特定的同步机制(如互斥锁、条件变量等)来协调它们的执行顺序,确保数据的一致性和程序的正确性。
当然优缺点总是并存的,这么牛B的线程也有其不好之处,那就是编程的复杂度。比如全局数据的共享问题、线程间的同步问题、各种锁的应用场景,等等。
函数函数
这第一节就先介绍线程创建的几个基本函数,也是看手册的一些心得,欢迎大家一起讨论。
函数名称 | 说明 |
ptherad_create(); | 在进程中创建一个线程 |
pthread_exit(), | 退出线程或终止线程。 |
pthread_cancel() | 向指定线程发送一个取消线程的请求 |
pthread_join() | 线程回收 |
pthread_self() | 获取当前线程的线程ID |
pthread_detach() | 标记指定线程的分离状态 |
pthread_equal() | 比较两个线程ID是否相同 |
pthread_attr_destroy() | 销毁线程属性 |
pthread_attr_init() | 初始化线程属性 |
pthread_create()
pthread_create()在进程中创建一个新的线程。
int pthread_create(pthread_t * restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*),void *restrict arg);
新线程将执行start_routine所引用的线程函数,“arg”是要传递给 start_routine 中的线程参数指针。在线程创建成功后,线程ID通过指针 thread 返回。
函数定义中,用到了“restrict"限定符,用于告诉编译器,在指针的生命周期内,它指向的对象只能通过这个特定的指针来访问。即该指针是唯一的,没有其他指针指向该指针所指向的内存段。
"thread":是pthread_t 类型指针,用来存储线程创建后保存线程ID的。
"attr" : 指定的线程属性,若其指针为空,则采用默认线程参数进行创建。
start_routine : 线程函数。
"arg" : 是传递给线程的参数。
注:编译时一定要引用“-pthread",否则会报错。
pthread_exit()
void pthread_exit(void *retval)
这个函数是用来终止调用它的线程的。指针 retval 所指向的数据会提供给任务何成功连接到终止线程的线程。一般情况下都是给NULL,如果是条件退出的话可以传递退出原因等参数。
pthread_cancel()
int pthread_cancel(pthread_t thread)
该函数用来向指定线程(thread)发送“取消线程”的请求。目标线程什么时候取消取决于线程的可取消状态和类型。这个主要用于要干掉这个线程,但还要保证线程安全,即线程要在可取消状态下被干掉,这样可以在一定程度上保护进程的稳定。
pthread_join()
int pthread_join(pthread_t thread, void **value_ptr);
该函数用来阻塞式等待线程 “thread” 终止,并将函数 pthread_exit() 函数所返回的值放到 value_ptr 所指向的位置。
pthread_self()
pthread_t pthread_self(void);
获取当前线程的线程ID,手册上说这是个不会出错的函数。
pthread_attr_init()
int pthread_attr_init(pthread_attr_t *attr)
依默认属性参数初始化指定的线程属性参数,初始化成功后会返回0,失败的话则会返回相应的错误码。只有初始化之后才可能通过相关的函数去配置属性参数。
pthread_attr_destroy()
int pthread_attr_destroy(pthread_attr_t *attr)
销毁指定的线程属性参数。成功返回0。失败则会返回相应的错误代码。
pthread_detach()
int pthread_detach(pthread_t thread)
将指定线程配置为分离状态,使其在线程结束时由系统直接回收线程所占用的资源。该函数并不会直接结束线程,它只是修改了线程结束时资源回收方式。
pthread_equal()
int pthread_equal(pthread_t t1, pthread_t t2);
比较两个线程ID是否是相等的,如果相等则会返回一个非零的值,反之则会返回0。这又是一个号称不会失败的函数,不过在手册中说当t1/t2 不是线程ID时可能会返回任何值。
示例
想来想去还是去抄了官方的示例过来,在其基础上稍做了一些修改,未尽之处大家多指教吧。
// #include <pthread.h>
// #include <stdio.h>
#include "test_ma.h"
#include <ctype.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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=%s\n", tinfo->thread_num, (void*)&p, tinfo->argv_string);
uargv = strdup(tinfo->argv_string);
if (uargv == NULL)
handle_error("strdup");
for (p = uargv; *p != '\0'; p++)
*p = toupper(*p);
return uargv;
}
int main(int argc, char* argv[])
{
// 定义变量s,tnum,opt,num_threads,tinfo,stack_size,res
int s, tnum, opt, num_threads = 0;
struct thread_info* tinfo;
pthread_attr_t attr;
int stack_size;
void* res;
/* The "-s" option specifies a stack size for our threads */
printf("Creating %d threads,argc[%d]\n", num_threads, argc);
// 设置stack_size为-1
stack_size = -1;
// 循环获取参数,argc为命令行参数个数,argv为命令行参数数组,当返回“s"时表时获取了一个字符串
while ((opt = getopt(argc, argv, "s:")) != -1) {
// 根据参数选择执行操作
switch (opt) {
case 's':
// 设置stack_size,将获得的这个字符串参数转换为10进制数
stack_size = strtoul(optarg, NULL, 0);
break;
default:
// 打印错误信息
fprintf(stderr, "Usage: %s [-s stack-size] arg...\n", argv[0]);
// 退出程序
exit(EXIT_FAILURE);
}
}
num_threads = argc - optind;
printf("Creating %d threads,argc[%d],optind[%d]\n", num_threads, argc, optind);
/* Initialize thread creation attributes */
// 初始化线程创建属性
s = pthread_attr_init(&attr);
if (s != 0)
handle_error_en(s, "pthread_attr_init");
/* Set stack size for each thread, if requested */
if (stack_size > 0) {
s = pthread_attr_setstacksize(&attr, stack_size);
if (s != 0)
handle_error_en(s, "pthread_attr_setstacksize");
}
/* Allocate memory for pthread_create() arguments */
tinfo = calloc(num_threads, sizeof(struct thread_info));
if (tinfo == NULL)
handle_error("calloc");
/* Create one thread for each command-line argument */
for (tnum = 0; tnum < num_threads; tnum++) {
tinfo[tnum].thread_num = tnum + 1;
tinfo[tnum].argv_string = argv[optind + tnum];
/* The pthread_create() call stores the thread ID into
corresponding element of tinfo[] */
if (tnum % 2)
s = pthread_create(&tinfo[tnum].thread_id, &attr, &thread_function, &tinfo[tnum]);
else
s = pthread_create(&tinfo[tnum].thread_id, &attr, &thread_start, &tinfo[tnum]);
if (s != 0)
handle_error_en(s, "pthread_create");
}
/* Destroy the thread attributes object, since it is no
longer needed */
s = pthread_attr_destroy(&attr);
if (s != 0)
handle_error_en(s, "pthread_attr_destroy");
/* Now join with each thread, and display its returned value */
for (tnum = 0; tnum < num_threads; tnum++) {
s = pthread_join(tinfo[tnum].thread_id, &res);
if (s != 0)
handle_error_en(s, "pthread_join");
printf("Joined with thread %d; returned value was %s\n", tinfo[tnum].thread_num, (char*)res);
free(res); /* Free memory allocated by thread */
}
free(tinfo);
exit(EXIT_SUCCESS);
}