unix环境高级编程学习——线程编程

[复制链接]
查看828 | 回复5 | 2023-9-15 09:54:46 | 显示全部楼层 |阅读模式
1. 线程编程概述
   

线程(Thread)是多任务处理中的一种实现模式。在Unix/Linux环境中,线程被视为轻量级的进程,它们共享相同的地址空间,允许程序中的不同部分同时执行。线程编程是一种高效的并发编程方式,它可以让程序员在单个进程中实现并发操作,提高程序的执行效率。
    线程和进程的联系和区别:
        线程和进程都是计算机科学中描述程序执行过程的基本概念,但它们在资源使用、执行过程和错误处理等方面存在明显的差异。

  • 资源使用:进程是操作系统资源分配的基本单位,它拥有独立的代码和数据空间(即程序上下文)。然而,同一类线程共享进程所拥有的全部资源,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间的切换开销相对较小。
  • 执行过程:进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位。每个进程都有自己的地址空间,至少有5种基本状态,它们是:初始态、执行态、等待状态、就绪状态和终止状态。然而,线程是CPU调度和分派的基本单位,线程必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。
  • 错误处理:当一个进程崩溃时,它对其他进程不会产生影响。然而,一个线程崩溃后整个进程都会死掉,因为线程是进程的一部分。所以,多进程比多线程更健壮。
    总的来说,进程和线程在资源使用、执行过程和错误处理等方面存在明显差异。进程拥有独立的资源,而线程则共享资源。此外,进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。在错误处理方面,进程比线程更健壮。
    线程编程的基本概念包括线程创建、线程终止、线程同步、线程通信等。下面我们将从这几个方面来详细介绍线程编程

2. 线程创建

在Unix/Linux环境中,创建线程的函数是pthread_create。该函数需要传入以下参数:

  •     thread:指向线程标识符的指针。
  •     attr:指向线程属性的指针,通常设置为NULL。
  •     start_routine:指向线程函数的指针。
  •     arg:传递给线程函数的参数。
以下是pthread_create函数的示例代码:
  1. #include <pthread.h>  
  2.   
  3. void* my_thread_func(void* arg) {  
  4.     // 线程函数代码  
  5.     return NULL;  
  6. }  
  7.   
  8. int main() {  
  9.     pthread_t my_thread;  
  10.     int ret = pthread_create(&my_thread, NULL, my_thread_func, NULL);  
  11.     if (ret != 0) {  
  12.         // 线程创建失败  
  13.         return -1;  
  14.     }  
  15.     // 等待线程结束  
  16.     pthread_join(my_thread, NULL);  
  17.     return 0;  
  18. }
复制代码

在上面的代码中,我们定义了一个名为my_thread_func的线程函数,并在main函数中通过pthread_create函数创建了一个名为my_thread的新线程来执行该函数。如果线程创建成功,pthread_create函数将返回0,否则返回错误码。最后,我们使用pthread_join函数等待my_thread线程结束。


3. 线程终止
在Unix/Linux环境中,终止线程的函数是pthread_exit。该函数需要传入一个指向void的指针,该指针将被用作线程的返回值。如果主线程中调用了pthread_join函数来等待子线程结束,那么该指针将被传递给pthread_join函数。以下是一个使用pthread_exit函数的示例代码:

  1. #include <pthread.h>  
  2.   
  3. void* my_thread_func(void* arg) {  
  4.     // 线程函数代码  
  5.     pthread_exit(NULL); // 终止线程并返回NULL  
  6. }  
  7.   
  8. int main() {  
  9.     pthread_t my_thread;  
  10.     int ret = pthread_create(&my_thread, NULL, my_thread_func, NULL);  
  11.     if (ret != 0) {  
  12.         // 线程创建失败  
  13.         return -1;  
  14.     }  
  15.     // 等待线程结束  
  16.     pthread_join(my_thread, NULL);  
  17.     return 0;  
  18. }
复制代码

4. 线程同步
在多线程编程中,线程同步是一种关键技术,它用于协调多个线程的执行顺序,以避免资源竞争和数据不一致的问题。在Unix/Linux环境中,我们可以使用以下线程同步技术:
4.1 互斥锁(Mutex)
互斥锁是一种最基本的线程同步机制,它可以保证在任意时刻,只有一个线程可以访问共享资源。在Unix/Linux中,我们可以使用pthread库提供的函数来创建和管理互斥锁。
以下是使用互斥锁的示例代码:

  1. #include <pthread.h>  
  2.   
  3. pthread_mutex_t mutex;  
  4. int shared_data = 0;  
  5.   
  6. void* thread_func(void* arg) {  
  7.     pthread_mutex_lock(&mutex); // 加锁  
  8.     shared_data += 1;  
  9.     printf("Thread %ld: shared_data = %d\n", pthread_self(), shared_data);  
  10.     pthread_mutex_unlock(&mutex); // 解锁  
  11.     return NULL;  
  12. }  
  13.   
  14. int main() {  
  15.     pthread_t thread1, thread2;  
  16.     pthread_mutex_init(&mutex, NULL); // 初始化互斥锁  
  17.     pthread_create(&thread1, NULL, thread_func, NULL);  
  18.     pthread_create(&thread2, NULL, thread_func, NULL);  
  19.     pthread_join(thread1, NULL);  
  20.     pthread_join(thread2, NULL);  
  21.     pthread_mutex_destroy(&mutex); // 销毁互斥锁  
  22.     return 0;  
  23. }
复制代码

4.2 条件变量(Condition Variables)
条件变量是一种更高级的线程同步机制,它允许线程等待某个条件成立。当条件成立时,被阻塞的线程将被唤醒。在Unix/Linux中,我们可以使用pthread库提供的函数来创建和管理条件变量。
以下是使用条件变量的示例代码


  1. #include <pthread.h>  
  2.   
  3. pthread_cond_t cond;  
  4. int shared_data = 0;  
  5.   
  6. void* thread_func(void* arg) {  
  7.     printf("Thread %ld: waiting for shared data...\n", pthread_self());  
  8.     pthread_cond_wait(&cond, &mutex); // 等待条件成立  
  9.     shared_data += 1;  
  10.     printf("Thread %ld: shared_data = %d\n", pthread_self(), shared_data);  
  11.     return NULL;  
  12. }  
  13.   
  14. int main() {  
  15.     pthread_t thread1, thread2;  
  16.     pthread_mutex_init(&mutex, NULL); // 初始化互斥锁  
  17.     pthread_cond_init(&cond, NULL); // 初始化条件变量  
  18.     pthread_create(&thread1, NULL, thread_func, NULL);  
  19.     pthread_create(&thread2, NULL, thread_func, NULL);  
  20.     pthread_join(thread1, NULL);  
  21.     pthread_join(thread2, NULL);  
  22.     pthread_mutex_destroy(&mutex); // 销毁互斥锁  
  23.     pthread_cond_destroy(&cond); // 销毁条件变量  
  24.     return 0;  
  25. }
复制代码

5. 线程通信
在多线程编程中,线程之间的通信是一个常见的问题。在Unix/Linux环境中,我们可以使用以下线程通信技术:
5.1 管道(Pipe)
管道是一种最简单的线程通信方式,它允许一个线程向另一个线程发送数据。在Unix/Linux中,我们可以使用pipe函数来创建一个管道,并使用write函数向管道写入数据,使用read函数从管道读取数据。
以下是使用管道的示例代码:

  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3.   
  4. int main() {  
  5.     int pipefd[2];  
  6.     pid_t pid;  
  7.     char buf[1024];  
  8.   
  9.     if (pipe(pipefd) == -1) {  
  10.         perror("pipe");  
  11.         return -1;  
  12.     }  
  13.     pid = fork();  
  14.     if (pid == -1) {  
  15.         perror("fork");  
  16.         return -1;  
  17.     } else if (pid == 0) { // 子进程  
  18.         close(pipefd[1]); // 关闭管道的写端  
  19.         read(pipefd[0], buf, sizeof(buf));  
  20.         printf("Child process received message: %s\n", buf);  
  21.         close(pipefd[0]);  
  22.     } else { // 父进程  
  23.         close(pipefd[0]); // 关闭管道的读端  
  24.         write(pipefd[1], "Hello from parent", 17);  
  25.         close(pipefd[1]);  
  26.     }  
  27.     return 0;  
  28. }
复制代码

5.2 消息队列(Message Queue)
消息队列是一种更高级的线程通信方式,它允许一个线程向另一个线程发送消息。在Unix/Linux中,我们可以使用msgget函数创建一个消息队列,并使用msgsnd函数向消息队列发送消息,使用msgrcv函数从消息队列接收消息。
以下是使用消息队列的示例代码:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <sys/ipc.h>  
  5. #include <sys/msg.h>  
  6.   
  7. #define MSG_SIZE 1024  
  8.   
  9. struct {  
  10.     long mtype;  
  11.     char mtext[MSG_SIZE];  
  12. } msgbuf;  
  13.   
  14. int main() {  
  15.     int msqid;  
  16.     key_t key;  
  17.     char* mtext = "Hello from parent";  
  18.     int mtextlen = strlen(mtext);  
  19.   
  20.     key = ftok(".", 'R'); // 获取消息队列的key值  
  21.     msqid = msgget(key, 0666 | IPC_CREAT); // 创建消息队列  
  22.     if (msqid == -1) {  
  23.         perror("msgget");  
  24.         return -1;  
  25.     }  
  26.     msgbuf.mtype = mtextlen; // 设置消息类型为长度  
  27.     strncpy(msgbuf.mtext, mtext, mtextlen); // 设置消息内容为mtext字符串  
  28.     if (msgsnd(msqid, &msgbuf, mtextlen + sizeof(long), 0) == -1) { // 发送消息到消息队列中  
  29.         perror("msgsnd");  
  30.         return -1;  
  31.     } else { // 从消息队列接收消息并打印出来  
  32.         if (msgrcv(msqid, &msgbuf, MSG_SIZE, mtextlen, 0) == -1) {  
  33.             perror("msgrcv");  
  34.             return -1; else printf("Received message: %s\n", msgbuf.mtext); close(msqid); } } }

复制代码



回复

使用道具 举报

CHENQIGUANG1998 | 2023-9-15 09:56:19 | 显示全部楼层
字多可能有手误,欢迎指正
回复 支持 反对

使用道具 举报

496199544 | 2023-9-15 11:05:48 来自手机 | 显示全部楼层
学习
回复

使用道具 举报

jkernet | 2023-9-15 12:27:32 | 显示全部楼层
学习打卡
回复

使用道具 举报

ai_mcu | 2023-9-15 13:33:29 | 显示全部楼层
打卡标记
明天总会更好
回复

使用道具 举报

LV36 | 2023-9-24 13:36:35 | 显示全部楼层
关注
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则