目录
返回目录
C语言多线程
多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。
- 基于进程的多任务处理是程序的并发执行。
- 基于线程的多任务处理是同一程序的片段的并发执行。
多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。
本教程假设您使用的是 Linux 操作系统,我们要使用 POSIX 编写多线程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多种类 Unix POSIX 系统上可用,比如 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。
返回目录
创建线程
下面的程序,我们可以用它来创建一个POSIX 线程:
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
在这里,pthread_create 创建一个新的线程,并让它可执行。下面是关于参数的说明:
目录
返回目录 C语言多线程 多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。
- 基于进程的多任务处理是程序的并发执行。
- 基于线程的多任务处理是同一程序的片段的并发执行。
多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。 本教程假设您使用的是 Linux 操作系统,我们要使用 POSIX 编写多线程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多种类 Unix POSIX 系统上可用,比如 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。 返回目录 创建线程 下面的程序,我们可以用它来创建一个POSIX 线程: #include <pthread.h>
pthread_create (thread, attr, start_routine, arg) 在这里,pthread_create 创建一个新的线程,并让它可执行。下面是关于参数的说明:
参数 | 描述 |
---|
thread | 指向线程标识符指针。 | attr | 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。 | start_routine | 线程运行函数起始地址,一旦线程被创建就会执行。 | arg | 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。 |
创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。 返回目录 终止线程 使用下面的程序,我们可以用它来终止一个 POSIX 线程: #include <pthread.h>
pthread_exit (status) 在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。 如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。 连接和分离线程 我们可以使用以下两个函数来连接或分离线程: pthread_join (threadid, status)
pthread_detach (threadid) pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连。pthread_join() 函数来等待线程的完成。 返回目录 注意 pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a, 在使用pthread_create创建线程时,在编译中要加-lpthread参数: gcc createThread.c -lpthread -o createThread.o
./createThread. Test 1 无参数传递的线程并发编程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | // 基于线程的并发编程 // Test_1 createThread #include <stdio.h> #include <pthread.h> /* * pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a, * 在使用pthread_create创建线程时,在编译中要加-lpthread参数: * gcc createThread.c -lpthread -o createThread.o * ./createThread.o * 加上上面两句以后编译成功,并输出结果 * */ #define NUM_Threads 5 // 线程的运行函数 void *PrintHello( void *arg) { printf ( "Hello,World of Thread in C!\n" ); return 0; } int main() { int i; int ret; // 定义线程的id变量,多个变量使用数组 pthread_t tids[NUM_Threads]; for (i=0; i<NUM_Threads; i++) { // 参数依次是: 创建的线程id,线程参数,调用的函数,传入的函数参数 ret = pthread_create(&tids[i], NULL, PrintHello, NULL); if (ret != 0) { printf ( "pthread_create error: error_code = \n" ); } } // 等各个线程推出后,进程才结束 pthread_exit(NULL); return 0; } /* * 在CLion(Ubuntu)中输出结果为 Hello,World of Thread in C! Hello,World of Thread in C! Hello,World of Thread in C! Hello,World of Thread in C! Hello,World of Thread in C! * */ |
Test 2 简单参数传递的线程并发编程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | // 基于线程的并发编程,向线程传递参数1 // Test_2_createThread #include <stdio.h> #include <pthread.h> #include <stdlib.h> #define NUM_Threads 5 // 线程的运行函数 void *PrintHelloId( void *threadid) { // 对传入的参数进行强制类型转换,由无类型指针变为整形指针,然后再读取 int tid = *(( int *)threadid); printf ( "Hello,World, Thread %d\n" ,tid); return 0; } int main() { pthread_t pthreads[NUM_Threads]; int i, rc; // 用数组存储i的数值 int indexes[NUM_Threads]; for (i=0; i<NUM_Threads; i++) { printf ( "main() : 创建线程 %d \n" ,i); indexes[i] = i; // 保存i的数值 // 在indexes传入参数的时候必须转换为无类型指针 rc = pthread_create(&pthreads[i], NULL, PrintHelloId, ( void *)&indexes[i]); if (0 != rc) { printf ( "Error: 无法创建线程!\n" ); exit (-1); } } pthread_exit(NULL); return 0; } /* * 在CLion(Ubuntu)中输出结果是 main() : 创建线程 0 main() : 创建线程 1 Hello,World, Thread 0 main() : 创建线程 2 Hello,World, Thread 1 main() : 创建线程 3 Hello,World, Thread 2 main() : 创建线程 4 Hello,World, Thread 3 Hello,World, Thread 4 * */ |
Test 3 结构体参数传递的线程并发编程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | // 基于线程的并发编程,向线程传递参数2(传递结构体) // Test_3_createThread #include <stdio.h> #include <pthread.h> #include <stdlib.h> #define NUM_Threads 5 typedef struct thread_data{ int threadid; char message; }THDATA,*PTHDATA; void * PrintHello( void * pthreadid) { PTHDATA tid = (PTHDATA)pthreadid; printf ( "This is Pthread : %d ;info : %c \n" ,tid->threadid, tid->message); return 0; } int main( void ) { pthread_t Pthread[NUM_Threads]; THDATA index[NUM_Threads]; int i, ret; for (i = 0; i < NUM_Threads; i++) { printf ( "main() : 创建线程 %d \n" ,i); index[i].threadid = i; index[i].message = 'A' +i%10; ret = pthread_create(&Pthread[i], NULL, PrintHello, ( void *)&index[i]); if (0 != ret) { printf ( "Error: 创建线程失败!\n" ); exit (-1); } } pthread_exit(NULL); return 0; } /* * 在CLion(Ubuntu)中输出结果是 main() : 创建线程 0 main() : 创建线程 1 This is Pthread : 0 ;info : A main() : 创建线程 2 main() : 创建线程 3 This is Pthread : 2 ;info : C main() : 创建线程 4 This is Pthread : 3 ;info : D This is Pthread : 4 ;info : E This is Pthread : 1 ;info : B * */ |
Test 4 线程的连接编程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | // 基于线程的并发编程,连接或分离线程 // Test_4_createThread // 2019年10月27日14:45:11 尚未完全搞明白 #include <stdio.h> #include <pthread.h> #include <stdlib.h> #define NUM_Pthread 5 void *PrintHello( void * pthreadid) { int tid = *(( int *)pthreadid); printf ( "Sleeping in thread %d ,...exiting \n" ,tid); return 0; } int main( void ) { int i, ret; pthread_t Pthread[NUM_Pthread]; pthread_attr_t attr; // 定义线程属性 void * status; int index[NUM_Pthread]; // 初始化并设置线程为可连接 pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for (i=0; i<NUM_Pthread; i++) { printf ( "main() : 创建线程 %d \n" ,i); index[i] = i; ret = pthread_create(&Pthread[i], NULL, PrintHello, ( void *)&index[i]); } // 删除属性,并等待其他线程 pthread_attr_destroy(&attr); for (i=0; i<NUM_Pthread; i++) { ret = pthread_join(Pthread[i], status); if (0 != ret) { printf ( "Error: unable to join,%d\n" ,ret); exit (-1); } printf ( "main(): complete thread id : %d" ,i); printf ( " exiting with status : %p\n" ,status); } printf ( "main() : program exiting.\n" ); pthread_exit(NULL); return 0; } |
返回目录 信号量机制 Test 5 信号量同步进行写入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | // 用信号量进行同步 #include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <semaphore.h> #define Len 100 // 设置输入内容长度 sem_t bin_sem; char work_area[Len]; // 存放输入内容 void *Thread_func( void *arg) { // 等待信号量有大于0的值然后退出 sem_wait(&bin_sem); while (0 != strncmp ( "end" , work_area, 3)) { printf ( "Input %ld characters\n" , strlen (work_area)-1); } return 0; } int main( void ) { int res; // 存放命令的返回值 pthread_t Pthread; // 创建线程 void *thread_result; // 存放线程处理结果 // 初始化信号量,并设置初始值为0 res = sem_init(&bin_sem, 0, 0); if (0 != res) { perror ( "Semaphore initialization failes" ); exit (EXIT_FAILURE); } // 创建新线程 0 res = pthread_create(&Pthread, NULL, Thread_func, NULL); if (0 != res) { perror ( "Thread creation failed" ); exit (EXIT_FAILURE); } printf ( "Enter 'end' to finish\n" ); // 当工作区内不是以end开头的字符串,则继续输入 while (0 != strncmp ( "end" , work_area, 3)) { // 以标准输入获取输入到工作区内 fgets (work_area, Len, stdin); sem_post(&bin_sem); // 信号量+1 } printf ( "\n Waiting for thread to finish...\n" ); // 等待线程结束 res = pthread_join(Pthread, &thread_result); if (0 != res) { perror ( "Thread join failed" ); exit (EXIT_FAILURE); } printf ( "Thread joined\n" ); sem_destroy(&bin_sem); // 销毁信号量 exit (EXIT_SUCCESS); return 0; } |
Test 6 互斥信号量实现对临界资源操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | // 用互斥信号量进行同步 #include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <string.h> #define Len 3 // 自增计算次数 #define NUM_Pthread 5 // 设置线程的长度 int count = 1; // 在数据段共享资源,多个进程抢占临界资源 // 对于临界资源,应该添加互斥锁 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *Thread_func( void *threadid) { int tid = *(( int *)threadid); int i, val; printf ( "Pthread ID : %d \n" ,tid); for (i=0; i<NUM_Pthread; i++) { pthread_mutex_lock(&mutex); val = count; printf ( "val = %d \n" ,val++); count = val; pthread_mutex_unlock(&mutex); } return 0; } int main( void ) { int res; // 存放命令的返回值 int i; pthread_t Pthread[NUM_Pthread]; // 创建线程 int index[NUM_Pthread]; for (i=0; i<NUM_Pthread; i++) { index[i] = i; // 创建线程 res = pthread_create(&Pthread[i], NULL, Thread_func, ( void *)&index[i]); if (0 != res) { printf ( "Error: 创建线程失败!\n" ); exit (-1); } } for (i=0; i<NUM_Pthread; i++) { // 汇合线程 pthread_join(Pthread[i], NULL); } printf ( "count = %d\n" ,count); pthread_exit(NULL); return 0; } // 在运行此程序无互斥锁时,我们不仅得到错误的答案,而且每次得到的答案都不相同 // 分析 // 当多个对等线程在一个处理器上并发运行时,机器指令以某种顺序完成,每个并发执行定义了线程中指令的某种顺序 |
| |
---|
| |
| |
| |
| |
创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。
返回目录
终止线程
使用下面的程序,我们可以用它来终止一个 POSIX 线程:
#include <pthread.h>
pthread_exit (status)
在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。
如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。
连接和分离线程
我们可以使用以下两个函数来连接或分离线程:
pthread_join (threadid, status)
pthread_detach (threadid)
pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连。pthread_join() 函数来等待线程的完成。
返回目录
注意
pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a, 在使用pthread_create创建线程时,在编译中要加-lpthread参数:
gcc createThread.c -lpthread -o createThread.o
./createThread.
Test 1 无参数传递的线程并发编程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | // 基于线程的并发编程 // Test_1 createThread #include <stdio.h> #include <pthread.h> /* * pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a, * 在使用pthread_create创建线程时,在编译中要加-lpthread参数: * gcc createThread.c -lpthread -o createThread.o * ./createThread.o * 加上上面两句以后编译成功,并输出结果 * */ #define NUM_Threads 5 // 线程的运行函数 void *PrintHello( void *arg) { printf ( "Hello,World of Thread in C!\n" ); return 0; } int main() { int i; int ret; // 定义线程的id变量,多个变量使用数组 pthread_t tids[NUM_Threads]; for (i=0; i<NUM_Threads; i++) { // 参数依次是: 创建的线程id,线程参数,调用的函数,传入的函数参数 ret = pthread_create(&tids[i], NULL, PrintHello, NULL); if (ret != 0) { printf ( "pthread_create error: error_code = \n" ); } } // 等各个线程推出后,进程才结束 pthread_exit(NULL); return 0; } /* * 在CLion(Ubuntu)中输出结果为 Hello,World of Thread in C! Hello,World of Thread in C! Hello,World of Thread in C! Hello,World of Thread in C! Hello,World of Thread in C! * */ |
Test 2 简单参数传递的线程并发编程实例
+ View Code
Test 3 结构体参数传递的线程并发编程实例
+ View Code
Test 4 线程的连接编程实例
+ View Code
返回目录
信号量机制
Test 5 信号量同步进行写入
+ View Code
Test 6 互斥信号量实现对临界资源操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | // 用互斥信号量进行同步 #include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <string.h> #define Len 3 // 自增计算次数 #define NUM_Pthread 5 // 设置线程的长度 int count = 1; // 在数据段共享资源,多个进程抢占临界资源 // 对于临界资源,应该添加互斥锁 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *Thread_func( void *threadid) { int tid = *(( int *)threadid); int i, val; printf ( "Pthread ID : %d \n" ,tid); for (i=0; i<NUM_Pthread; i++) { pthread_mutex_lock(&mutex); val = count; printf ( "val = %d \n" ,val++); count = val; pthread_mutex_unlock(&mutex); } return 0; } int main( void ) { int res; // 存放命令的返回值 int i; pthread_t Pthread[NUM_Pthread]; // 创建线程 int index[NUM_Pthread]; for (i=0; i<NUM_Pthread; i++) { index[i] = i; // 创建线程 res = pthread_create(&Pthread[i], NULL, Thread_func, ( void *)&index[i]); if (0 != res) { printf ( "Error: 创建线程失败!\n" ); exit (-1); } } for (i=0; i<NUM_Pthread; i++) { // 汇合线程 pthread_join(Pthread[i], NULL); } printf ( "count = %d\n" ,count); pthread_exit(NULL); return 0; } // 在运行此程序无互斥锁时,我们不仅得到错误的答案,而且每次得到的答案都不相同 // 分析 // 当多个对等线程在一个处理器上并发运行时,机器指令以某种顺序完成,每个并发执行定义了线程中指令的某种顺序 |