【从零开始的嵌入式生活】并发程序设计2——线程专题

简介: 【从零开始的嵌入式生活】并发程序设计2——线程专题

文章目录

线程基础

进程特点

线程

线程特点

线程共享资源

线程私有资源

Linux线程库

线程库功能

线程创建 – pthread_create

线程回收 – pthread_join

线程结束 – pthread_exit

其他函数

线程间通信

同步

信号量

Posix 信号量

信号量初始化 – sem_init

信号量 – P / V 操作

线程通信——互斥

互斥锁初始化 – pthread_mutex_init

申请锁 – pthread_mutex_lock

释放锁 – pthread_mutex_unlock

写在最后

线程基础

QQ的多个功能:


接收输入

通讯

显示界面

为了实现多个功能可以使用线程。


进程特点

进程有独立的地址空间(不能互相访问)

Linux为每个进程创建task_struct

每个进程都参与内核调度,互不影响

线程

进程在切换时系统开销大,很多操作系统引入了轻量级进程LWP


同一进程中的线程共享相同地址空间

Linux不区分进程、线程

线程特点

通常线程指的是共享相同地址空间的多个任务


使用多线程的好处:


大大提高了任务切换的效率

避免了额外的TLB & cache的刷新(进程上下文)

线程共享资源

可执行的指令

静态数据(全局变量)

进程中打开的文件描述符

当前工作目录

用户ID

用户组ID

线程私有资源

线程ID (TID)

PC(程序计数器)和相关寄存器

堆栈

错误号 (errno)

优先级

执行状态和属性

Linux线程库

线程库功能

pthread线程库中提供了如下基本操作


创建线程

回收线程

结束线程

同步和互斥机制


信号量

互斥锁

线程创建 – pthread_create

#include  <pthread.h>
int  pthread_create(pthread_t *thread, const
      pthread_attr_t *attr, void *(*routine)(void *), void *arg);


成功返回0,失败时返回错误码

thread 线程对象

attr 线程属性,NULL代表默认属性

routine 线程执行的函数

arg 传递给routine的参数 ,参数是void * ,注意传递参数格式,

示例代码:


#include <stdio.h>
#include <pthread.h>
#include <string.h>
void * function1(void *arg){
       printf("this is thread function\n");
}
int main(){
       pthread_t tid;
       int err;
       err = pthread_create(&tid, NULL, function1, NULL);
       sleep(1);
       if(err != 0){
               printf("create thread:%s\n",strerror(err));
       }
       return 0;
}


注意:编译的时候需要加入-lpthread链接线程库!在线程中exit就会全部结束!


线程回收 – pthread_join

#include  <pthread.h>
int  pthread_join(pthread_t thread, void **retval);//*tid 和线程的返回值


成功返回0,失败时返回错误码

thread 要回收的线程对象

调用线程阻塞直到thread结束

*retval 接收线程thread的返回值


线程结束 – pthread_exit

结束当前线程

retval可被其他线程通过pthread_join获取

线程私有资源被释放

示例代码:


#include <stdio.h>
#include <pthread.h>
#include <string.h>
void * function1(void *arg){
       printf("this is thread function\n");
       sleep(1);
       pthread_exit("funct exit");
}
int main(){
       pthread_t tid;
       int err, i;
       err = pthread_create(&tid, NULL, function1, NULL);
       void *retval;
       pthread_join(tid, &retval);
       printf("retval = %s",(char *)retval);
       return 0;
}


其他函数

这部分就自己看man手册尝试用用吧


Pthread_join

Pthread_cancel

pthread_testcancel

pthread_setcancelstate

pthread_setcanceltype

Pthead_detach

pthread_attri_init

取消一个线程


int pthread_cancel(pthread_t thread);

void pthread_testcancel(void);

int pthread_setcancelstate(int state, int *oldstate);

PTHREAD_CANCEL_ENABLE

PTHREAD_CANCEL_DISABLE

int pthread_setcanceltype(int type, int *oldtype);

PTHREAD_CANCEL_DEFERRED

PTHREAD_CANCEL_ASYNCHRONOUS

线程间通信

线程共享同一进程的地址空间

优点:线程间通信很容易 通过全局变量交换数据

缺点:多个线程访问共享数据时需要同步或互斥机制

同步

同步(synchronization)指的是多个任务按照约定的先后次序相互配合完成一件事情

1968年,Edsgar Dijkstra基于信号量的概念 提出了一种同步机制

由信号量来决定线程是继续运行还是阻塞等待


信号量

信号量代表某一类资源,其值表示系统中该资源的数量

信号量是一个受保护的变量,只能通过三种操作来访问


初始化

P操作(申请资源)

V操作(释放资源)

Posix 信号量

posix中定义了两类信号量:


无名信号量(基于内存的信号量)

有名信号量

信号量初始化 – sem_init

#include  <semaphore.h>
int  sem_init(sem_t *sem, int pshared, unsigned int val);


成功时返回0,失败时EOF

sem 指向要初始化的信号量对象

pshared 0 – 线程间 1 – 进程间

val 信号量初值


信号量 – P / V 操作

#include  <semaphore.h>
int  sem_wait(sem_t  *sem);       P操作
int  sem_post(sem_t  *sem);       V操作


成功时返回0,失败时返回EOF

sem 指向要操作的信号量对象

示例代码:


#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#include <stdlib.h>
char buf[100];
sem_t sem;
void *write(void *arg){
        while(1){
                fgets(buf, 20, stdin);
                sem_post(&sem);
        }
}
void *read(void *arg){
        while(1){
                sem_wait(&sem);
                printf("%s\n", buf);
                memset(buf, 0, sizeof(buf));
        }
}
int main(){
        pthread_t tid1, tid2;
        int re;
        sem_init(&sem, 0 , 0);
        re = pthread_create(&tid1, NULL, write, NULL);
        if(re != 0){
                printf("pthread_create: %s\n",strerror(re));
                exit(0);
        }
        re = pthread_create(&tid2, NULL, read, NULL);
        if(re != 0){
                printf("pthread_create: %s\n",strerror(re));
                exit(0);
        }
        while(1){
                sleep(1);
        }
}

线程通信——互斥

临界资源

一次只允许一个任务(进程、线程)访问的共享资源

临界区

访问临界区的代码

互斥机制

mutex互斥锁

任务访问临界资源前申请锁,访问完后释放锁

互斥锁初始化 – pthread_mutex_init

#include  <pthread.h>
int  pthread_mutex_init(pthread_mutex_t *mutex,
      const pthread_mutexattr_t *  attr);


成功时返回0,失败时返回错误码

mutex 指向要初始化的互斥锁对象

attr 互斥锁属性,NULL表示缺省属性

man 函数出现 No manual entry for pthread_mutex_xxx解决办法

apt-get install manpages-posix-dev


申请锁 – pthread_mutex_lock

#include  <pthread.h>
int  pthread_mutex_lock(pthread_mutex_t *mutex);


成功时返回0,失败时返回错误码

mutex 指向要初始化的互斥锁对象

如果无法获得锁,任务阻塞

释放锁 – pthread_mutex_unlock

#include  <pthread.h>
int  pthread_mutex_unlock(pthread_mutex_t *mutex);


成功时返回0,失败时返回错误码

mutex 指向要初始化的互斥锁对象

执行完临界区要及时释放锁

示例代码:


#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
FILE *fp;
pthread_mutex_t mutex;
void *write1(void* arg){
        int a=0;
        a = (int)arg;
        int len,i;
        char *c1 = "Hello world\n";
        char *c2;
        len = strlen(c1);
        int td = pthread_self();
        pthread_detach(pthread_self());
        c2 = c1;
        while(1){
           pthread_mutex_lock(&mutex);
           for(i=0;i<len;i++){
                  fputc(*c1,fp);
                  fflush(fp);
                  c1++;
                  usleep(10000);
           }
           pthread_mutex_unlock(&mutex);
           c1 = c2;
           sleep(1);
        }
}
void *write2(void* arg){
        int a=0;
        a = (int)arg;
        int len,i;
        char *c1 = "How are your\n";
        char *c2;
        c2 = c1;
        len = strlen(c1);
        int td = pthread_self();
        pthread_detach(pthread_self());
        while(1){
           pthread_mutex_lock(&mutex);
           for(i=0;i<len;i++){
                  fputc(*c1,fp);
                  fflush(fp);
                  c1++;
                  usleep(10000);
           }
           pthread_mutex_unlock(&mutex);
           c1 = c2;
           sleep(1);
        }
}
int main(){
        int re,i=0;
        pthread_t tid1,tid2;
        fp = fopen("1.txt","w");
        if(!fp){
                perror("fopen");
                return -1;
        }
        pthread_mutex_init(&mutex,NULL);
        re = pthread_create(&tid1, NULL,write1, (void *)i);
        pthread_detach(tid1);
        if(re!=0){
                printf("pthread_create:%s\n",strerror(re));
                exit(0);
        }
        re = pthread_create(&tid2, NULL,write2, (void *)i);
        pthread_detach(tid2);
        if(re!=0){
                printf("pthread_create:%s\n",strerror(re));
                exit(0);
        }
        while(1){
                sleep(1);
        }
}


写在最后

今天讲完了线程相关的内容,这部分内容比较枯燥,建议跟着我的示例代码敲一敲,然后之前我也写过相关的内容建议参照着一起看【C语言有什么用?②】制作一个多线程词频统计工具所有文件我都放在了gitee哦,需要自取,我尽量一天一更,大家和我一起变强呀!明天开始进入进程间通信专题!最后三连即可提高学习效率!!!


另外我在更新的就是算法笔记的一些例题笔记,这个系列是用于提高我的算法能力,如果有兴趣对算法领域感兴趣找不到合适的入门文章也可以追更,如果我更新的太慢了请大家点赞收藏,一键三连才能更有更新的动力呀0.0


相关文章
|
1月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
155 0
|
23天前
|
安全
List并发线程安全问题
【10月更文挑战第21天】`List` 并发线程安全问题是多线程编程中一个非常重要的问题,需要我们认真对待和处理。只有通过不断地学习和实践,我们才能更好地掌握多线程编程的技巧和方法,提高程序的性能和稳定性。
130 59
|
3月前
|
Java 开发者
解锁并发编程新姿势!深度揭秘AQS独占锁&ReentrantLock重入锁奥秘,Condition条件变量让你玩转线程协作,秒变并发大神!
【8月更文挑战第4天】AQS是Java并发编程的核心框架,为锁和同步器提供基础结构。ReentrantLock基于AQS实现可重入互斥锁,比`synchronized`更灵活,支持可中断锁获取及超时控制。通过维护计数器实现锁的重入性。Condition接口允许ReentrantLock创建多个条件变量,支持细粒度线程协作,超越了传统`wait`/`notify`机制,助力开发者构建高效可靠的并发应用。
90 0
|
14天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
1月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
27 1
|
3月前
|
算法 Java
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
该博客文章综合介绍了Java并发编程的基础知识,包括线程与进程的区别、并发与并行的概念、线程的生命周期状态、`sleep`与`wait`方法的差异、`Lock`接口及其实现类与`synchronized`关键字的对比,以及生产者和消费者问题的解决方案和使用`Condition`对象替代`synchronized`关键字的方法。
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
|
2月前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
2月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
59 0
|
2月前
|
C语言
C语言 网络编程(九)并发的UDP服务端 以线程完成功能
这是一个基于UDP协议的客户端和服务端程序,其中服务端采用多线程并发处理客户端请求。客户端通过UDP向服务端发送登录请求,并根据登录结果与服务端的新子线程进行后续交互。服务端在主线程中接收客户端请求并创建新线程处理登录验证及后续通信,子线程创建新的套接字并与客户端进行数据交换。该程序展示了如何利用线程和UDP实现简单的并发服务器架构。
|
3月前
|
Rust 并行计算 安全
揭秘Rust并发奇技!线程与消息传递背后的秘密,让程序性能飙升的终极奥义!
【8月更文挑战第31天】Rust 以其安全性和高性能著称,其并发模型在现代软件开发中至关重要。通过 `std::thread` 模块,Rust 支持高效的线程管理和数据共享,同时确保内存和线程安全。本文探讨 Rust 的线程与消息传递机制,并通过示例代码展示其应用。例如,使用 `Mutex` 实现线程同步,通过通道(channel)实现线程间安全通信。Rust 的并发模型结合了线程和消息传递的优势,确保了高效且安全的并行执行,适用于高性能和高并发场景。
59 0

热门文章

最新文章