嵌入式系统多线程学习笔记

1) 标题最简单的多线程程序,不存在互斥


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>     /*Unix标准函数定义*/
void * thread1(void *arg)
{
   while(1)
   {
      printf("hello world!\n");
      sleep(1);
    }
}
int main()
{
   pthread_t id;
   if(pthread_create(&id,NULL,thread1,NULL)!=0)
   {
      perror("pthread create error!\n");
      exit(1);
   }
   while(1)
   {
      
   }
   //pause(); 
   return 0;
}

注意编译的时候必须增加 –lpthread 选项,否则是不能编译通过的,因为在linux中默认编译时不引用多线程的库的,这样才能在编译的时候调用多线程的库。
main:main.o
$(CC) main.o -o thread -lpthread
main.o:main.c
$(CC) -c main.c -o main.o -lpthread
clean:
$(RM) *.o thread

2) 使用信号量进行多线程编程 (适用于无规则,不控制,仅保证不冲突即可,互不干扰)

#include <pthread.h>
int pthread_mutex_init(phtread_mutex_t *mutex, const pthread_mutexattr_t *restrict attr);
该函数初始化一个互斥量,第一个参数 *mutex是改互斥量指针,第二个参数 *restrict attr为控制互斥量的属性,一般为NULL。当函数成功后会返回0,代表初始化互斥量成功。
  初始化互斥量也可以调用宏来快速初始化,代码如下:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER;
互斥量加锁/解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功:返回0
lock函数与unlock函数分别为加锁解锁函数,只需要传入已经初始化好的pthread_mutex_t互斥量指针。成功后会返回0。
  当某一个线程获得了执行权后,执行lock函数,一旦加锁成功后,其余线程遇到lock函数时候会发生阻塞,直至获取资源的线程执行unlock函数后。unlock函数会唤醒其他正在等待互斥量的线程。
  特别注意的是,当获取lock之后,必须在逻辑处理结束后执行unlock,否则会发生死锁现象!导致其余线程一直处于阻塞状态,无法执行下去。在使用互斥量的时候,尤其要注意使用pthread_cancel函数,防止发生死锁现象!
int pthread_cancel(pthread_t thread);函数用于线程被动退出,该函数传入一个tid号,会强制退出该tid所指向的线程,若成功执行会返回0
线程的创建
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
   *thread:用来保存新建线程的线程号
   *attr:表示线程的属性,一般传入NULL表示默认属性
   *(start_routine) (void ):一个函数指针,就是线程执行的函数。这个函数返回值为void,形参为void
   *arg:表示为向线程处理函数传入的参数,若不传入,可用NULL填充


#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>	/*信号量操作要包含的库*/
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<semaphore.h>   /*使用信号量PV要包含的库 */
struct  message
{
    int fd;
};
sem_t sem[1];
void * thread1(void *arg)
{
    struct message msg =  *((struct message * )arg);
    int fd= msg.fd;
    while(1)
{
/* */
        sem_wait(&sem[0]);
        printf("write 111\n");
        write(fd,"111\n",4);
        sem_post(&sem[0]);
        sleep(1);
    }
}
void * thread2(void *arg)
{
     struct message msg =  *((struct message * )arg);
    int fd= msg.fd;
    while(1)
    {
        sem_wait(&sem[0]);
         printf("write 222\n");
        write(fd,"222\n",4);
        sem_post(&sem[0]);
        sleep(1);
    }
}
void *thread3(void *arg)
{
     struct message msg =  *((struct message * )arg);
    int fd= msg.fd;
    while(1)
    {
        sem_wait(&sem[0]);
        printf("write 333\n");
        write(fd,"333\n",4);
        sem_post(&sem[0]);
        sleep(1);
    }
}
int main()
{
    pthread_t id1;
    pthread_t id2;
    pthread_t id3;
    struct message msg;
    int fd;
    fd=open("./a.txt",O_RDWR | O_CREAT | O_APPEND,0644);
    if(fd<0)
    {
        perror("open file error!");
        exit(1);
    }
msg.fd=fd;
/*初始化一个信号量
第一个参数*sem传入sem_t类型指针
第二个参数pshared传入0代表线程控制,否则为进程控制
 第三个参数value表示信号量的初始值,0代表阻塞,1代表运行
 待初始化结束信号量后,若执行成功会返回0
*/
    sem_init(&sem[0],0,1);
     
    if(pthread_create(&id1,NULL,thread1,(void *)(&msg)) != 0)
    {
        perror("thread create error!\n");
        exit(1);
    }
    if(pthread_create(&id2,NULL,thread2,(void *)(&msg)) != 0)
    {
        perror("thread create error!\n");
        exit(1);
    }
   if(pthread_create(&id3,NULL,thread3,(void *)(&msg)) != 0)
    {
        perror("thread create error!\n");
        exit(1);
    }
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    pthread_join(id3,NULL);
    return 0;
}

3) 多线程互斥操作(适合于主从线程,从线程可有主线程控制的情况,使用信号量控制)

这个实际上和2)差不多就是指定一个主循环运行一段代码,相当于一个线程,然后在主循环中给出从线程运行的通知,让从线程执行。
https://blog.csdn.net/weixin_43444989/article/details/123106933
初始化互斥量
#include <pthread.h>
int pthread_mutex_init(phtread_mutex_t *mutex, const pthread_mutexattr_t *restrict attr);
  该函数初始化一个互斥量,第一个参数 *mutex是改互斥量指针,第二个参数 *restrict attr为控制互斥量的属性,一般为NULL。当函数成功后会返回0,代表初始化互斥量成功。
  初始化互斥量也可以调用宏来快速初始化,代码如下:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITALIZER;
或者
pthread_mutex_t mut
pthread_mutex_init(&mut,NULL);
互斥量加锁/解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功:返回0
  lock函数与unlock函数分别为加锁解锁函数,只需要传入已经初始化好的pthread_mutex_t互斥量指针。成功后会返回0。
  当某一个线程获得了执行权后,执行lock函数,一旦加锁成功后,其余线程遇到lock函数时候会发生阻塞,直至获取资源的线程执行unlock函数后。unlock函数会唤醒其他正在等待互斥量的线程。
  特别注意的是,当获取lock之后,必须在逻辑处理结束后执行unlock,否则会发生死锁现象!导致其余线程一直处于阻塞状态,无法执行下去。在使用互斥量的时候,尤其要注意使用pthread_cancel函数,防止发生死锁现象!
int pthread_cancel(pthread_t thread);函数用于线程被动退出,该函数传入一个tid号,会强制退出该tid所指向的线程,若成功执行会返回0
线程的创建
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
   *thread:用来保存新建线程的线程号
   *attr:表示线程的属性,一般传入NULL表示默认属性
   *(start_routine) (void ):一个函数指针,就是线程执行的函数。这个函数返回值为void,形参为void
   *arg:表示为向线程处理函数传入的参数,若不传入,可用NULL填充
程序功能:从主线程中接收键盘输入内容保存在全局变量中,在子线程中将变量中的内容输出

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
static char g_buf[1000];//用来保存键盘输入内容的全局变量
static sem_t g_sem;
//也可以使用 pthread_mutex_init(&g_tMutex, NULL); 来初始化互斥量
static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static void *my_thread_func (void *data)
{
	while (1)
	{
		/* 等待通知 */
		sem_wait(&g_sem);
		/* 打印 */
		pthread_mutex_lock(&g_tMutex);
		printf("recv: %s\n", g_buf);
		pthread_mutex_unlock(&g_tMutex);
	}
	return NULL;
}
int main(int argc, char **argv)
{
	pthread_t tid;
	int ret;
	char buf[1000];
	//g_sem:sem_t类型变量 0:线程控制 0:信号量的初始值,0代表阻塞 
	sem_init(&g_sem, 0, 0);
	
	/* 1. 创建"接收线程" */
	//tid:保存新建的线程号 NULL:默认属性 my_thread_func:函数指针 NULL:无参数
	ret = pthread_create(&tid, NULL, my_thread_func, NULL);
	if (ret)
	{
		printf("pthread_create err!\n");
		return -1;
	}
	/* 2. 主线程读取标准输入, 发给"接收线程" */
	while (1)
	{
		//获取标准输入存入buf
		fgets(buf, 1000, stdin);
		//加锁成功会返回0,一旦加锁成功其他线程想给同一个互斥量加锁会发生阻塞
		pthread_mutex_lock(&g_tMutex);
		memcpy(g_buf, buf, 1000);
		//解锁成功会返回0,一旦解锁成功,函数会唤醒其他正在等待互斥量的线程
		pthread_mutex_unlock(&g_tMutex);
		/* 通知接收线程 */
		sem_post(&g_sem);
	}
	return 0;
}

4) 使用条件变量控制线程

创建和销毁条件变量
#include <pthread.h>
// 初始化条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);//cond_attr通常为NULL
// 销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
这些函数成功时都返回0
等待条件变量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t mutex);
  这需要结合互斥量一起使用,示例代码如下:
pthread_mutex_lock(&g_tMutex);
pthread_cond_wait(&g_tConVar, &g_tMutex); // 如果条件不满足则,会unlock g_tMutex
// 条件满足后被唤醒,会lock g_tMutex
/
操作临界资源 */
pthread_mutex_unlock(&g_tMutex);
  pthread_cond_wait总和一个互斥锁结合使用。在调用pthread_cond_wait前要先获取锁,因为pthread_cond_wait函数执行时先自动释放指定的锁,然后等待条件变量的变化,在函数调用返回之前,自动将指定的互斥量重新锁住。
通知条件变量
int pthread_cond_signal(pthread_cond_t *cond);
  pthread_cond_signal函数只会唤醒一个等待cond条件变量的线程,示例代码如下:
pthread_cond_signal(&g_tConVar);
  pthread_cond_signal通过条件变量cond发送消息,若多个消息在等待,它只唤醒一个
  调用pthread_cond_signal后要立刻释放互斥锁,因为pthread_cond_wait的最后一步是要将指定的互斥量重新锁住,如果pthread_cond_signal之后没有释放互斥锁,pthread_cond_wait仍然要阻塞。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
static char g_buf[1000];
static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
//也可以使用pthread_cond_init(g_tConVar,NULL);来初始化条件变量
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;
static void *my_thread_func (void *data)
{
	while (1)
	{
		pthread_mutex_lock(&g_tMutex);
		/*
			pthread_cond_wait函数先释放g_tMutex
			等待条件变量g_tConVar变化后在函数返回之前重新锁定g_tMutex
		*/
		pthread_cond_wait(&g_tConVar, &g_tMutex);	
		/* 打印 */
		printf("recv: %s\n", g_buf);
		pthread_mutex_unlock(&g_tMutex);
	}
	return NULL;
}
int main(int argc, char **argv)
{
	pthread_t tid;
	int ret;
	char buf[1000];
	
	/* 1. 创建"接收线程" */
	ret = pthread_create(&tid, NULL, my_thread_func, NULL);
	if (ret)
	{
		printf("pthread_create err!\n");
		return -1;
	}
	/* 2. 主线程读取标准输入, 发给"接收线程" */
	while (1)
	{
		fgets(buf, 1000, stdin);
		pthread_mutex_lock(&g_tMutex);
		memcpy(g_buf, buf, 1000);
		pthread_cond_signal(&g_tConVar); /* 通知接收线程 */
		//调用pthread_cond_signal后要立刻释放互斥锁
		pthread_mutex_unlock(&g_tMutex);
	}
	return 0;
}

5) 向线程传递参数-整型值

pthread_create()的最后一个参数的为void类型的数据,表示可以向线程传递一个void数据类型的参数,线程的回调函数中可以获取该参数,下面的例子举例了如何向线程传入变量地址与变量值。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
void *fun1(void *arg)
{
	//(int *)arg:	a的值
	//arg			:a的地址
	printf("%s:arg = %d Addr = %p\n",__FUNCTION__,*(int *)arg,arg);
}
void *fun2(void *arg)
{
	//(int)(long)arg:a的值
	//arg			:输出是16进制a的值
	printf("%s:arg = %d Addr = %p\n",__FUNCTION__,(int)(long)arg,arg);
}
int main()
{
	pthread_t tid1,tid2;
	int a = 50;
	//传递变量a的地址
	int ret = pthread_create(&tid1,NULL,fun1,(void *)&a);
	if(ret != 0){
		perror("pthread_create");
		return -1;
	}
	//传递变量a的值,针对不同位数机器,指针对其字数不同,需要int转化为long再转指针,否则可能会发生警告
	ret = pthread_create(&tid2,NULL,fun2,(void *)(long)a);
	if(ret != 0){
		perror("pthread_create");
		return -1;
	}
	sleep(1);
	printf("%s:a = %d Add = %p \n",__FUNCTION__,a,&a);
	return 0;
}

6) 传递结构体参数给线程

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
struct Stu{
	int Id;
	char Name[32];
	float Mark;
};
void *fun1(void *arg)
{
	struct Stu *tmp = (struct Stu *)arg;
	printf("%s:Id = %d Name = %s Mark = %.2f\n",__FUNCTION__,tmp->Id,tmp->Name,tmp->Mark);
	
}
int main()
{
	pthread_t tid1,tid2;
	struct Stu stu;
	stu.Id = 10000;
	strcpy(stu.Name,"ZhangSan");
	stu.Mark = 94.6;
	int ret = pthread_create(&tid1,NULL,fun1,(void *)&stu);
	if(ret != 0){
		perror("pthread_create");
		return -1;
	}
	printf("%s:Id = %d Name = %s Mark = %.2f\n",__FUNCTION__,stu.Id,stu.Name,stu.Mark);
	sleep(1);
	return 0;
}

7) 线程的退出与回收

线程的退出情况有三种:第一种是进程结束,进程中所有的线程也会随之结束。第二种是通过函数pthread_exit来主动的退出线程。第三种被其他线程调用pthread_cancel来被动退出。
线程主动退出
#include <pthread.h>
void pthread_exit(void retval);
  pthread_exit函数为线程退出函数,在退出时候可以传递一个void
类型的数据带给主线程,若选择不传出数据,可将参数填充为NULL。
线程被动退出
线程被动退出,其他线程使用该函数让另一个线程退出
#include <pthread.h>
int pthread_cancel(pthread_t thread);
成功:返回0
  该函数传入一个tid号,会强制退出该tid所指向的线程,若成功执行会返回0。
线程资源回收(阻塞方式)
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
  该函数为线程回收函数,默认状态为阻塞状态,直到成功回收线程后才返回。第一个参数为要回收线程的tid号,第二个参数为线程回收后接受线程传出的数据。
线程资源回收(非阻塞方式)
#define _GNU_SOURCE
#include <pthread.h>
int pthread_tryjoin_np(pthread_t thread, void **retval);
  该函数为非阻塞模式回收函数,通过返回值判断是否回收掉线程,成功回收则返回0,其余参数与pthread_join一致。
程序实例
程序功能:向子线程中传入变量在子线程中自加后子线程主动退出将自加后变量返回给主线程,主线程以阻塞方式回收子线程资源

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
void *fun1(void *arg)
{
	static int tmp = 0;//必须要static修饰,否则pthread_join无法获取到正确值
	tmp = *(int *)arg;
	tmp+=100;
	printf("%s:Addr = %p tmp = %d\n",__FUNCTION__,&tmp,tmp);
	//线程主动退出
	pthread_exit((void *)&tmp);
}
int main()
{
	pthread_t tid1;
	int a = 50;
	void *Tmp = NULL;//用来接收线程回收后传递出来的数据
	int ret = pthread_create(&tid1,NULL,fun1,(void *)&a);
	if(ret != 0){
		perror("pthread_create");
		return -1;
	}
	//以阻塞方式完成线程资源回收
	pthread_join(tid1,&Tmp);
	printf("%s:Addr = %p Val = %d\n",__FUNCTION__,Tmp,*(int *)Tmp);
	return 0;
}
使用非阻塞方式回收线程
#define _GNU_SOURCE 
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
void *fun(void *arg)
{
	printf("Pthread:%d Come !\n",(int )(long)arg+1);
	pthread_exit(arg);
}
int main()
{
	int ret,i,flag = 0;
	void *Tmp = NULL;
	pthread_t tid[3];
	for(i = 0;i < 3;i++){//创建3个线程
		ret = pthread_create(&tid[i],NULL,fun,(void *)(long)i);
		if(ret != 0){
			perror("pthread_create");
			return -1;
		}
	}
	while(1){//通过非阻塞方式收回线程,每次成功回收一个线程变量自增,直至3个线程全数回收
		for(i = 0;i <3;i++){
			if(pthread_tryjoin_np(tid[i],&Tmp) == 0){
				printf("Pthread : %d exit !\n",(int )(long )Tmp+1);
				flag++;	
			}
		}
		if(flag >= 3) break;
	}
	return 0;
}

使用被动退出方式在线程1中kill线程2

#define _GNU_SOURCE 
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
void *fun1(void *arg)
{
	printf("Pthread:1 come!\n");
	while(1){
		sleep(1);
	}
}
void *fun2(void *arg)
{
	printf("Pthread:2 come!\n");
	pthread_cancel((pthread_t )(long)arg);//杀死线程1,使之强制退出
	pthread_exit(NULL);
}
int main()
{
	int ret,i,flag = 0;
	void *Tmp = NULL;
	pthread_t tid[2];
	ret = pthread_create(&tid[0],NULL,fun1,NULL);
	if(ret != 0){
		perror("pthread_create");
		return -1;
	}
	sleep(1);
	ret = pthread_create(&tid[1],NULL,fun2,(void *)tid[0]);//传输线程1的线程号
	if(ret != 0){
		perror("pthread_create");
		return -1;
	}
	while(1){//通过非阻塞方式收回线程,每次成功回收一个线程变量自增,直至2个线程全数回收
		for(i = 0;i <2;i++){
			if(pthread_tryjoin_np(tid[i],NULL) == 0){
				printf("Pthread : %d exit !\n",i+1);
				flag++;	
			}
		}
		if(flag >= 2) break;
	}
	return 0;
}

创作不易,欢迎点赞关注. 下次会发布一个232的测试程序,用多线程方式编写.敬请期待.

/************************************************ * * The classic producer-consumer example. * Illustrates mutexes and conditions. * by Zou jian guo * 2003-12-22 * *************************************************/ #include #include #include #include "pthread.h" #define BUFFER_SIZE 16 /* Circular buffer of integers. */ struct prodcons { int buffer[BUFFER_SIZE]; /* the actual data */ pthread_mutex_t lock; /* mutex ensuring exclusive access to buffer */ int readpos, writepos; /* positions for reading and writing */ pthread_cond_t notempty; /* signaled when buffer is not empty */ pthread_cond_t notfull; /* signaled when buffer is not full */ }; /*--------------------------------------------------------*/ /* Initialize a buffer */ void init(struct prodcons * b) { pthread_mutex_init(&b->lock, NULL); pthread_cond_init(&b->notempty, NULL); pthread_cond_init(&b->notfull, NULL); b->readpos = 0; b->writepos = 0; } /*--------------------------------------------------------*/ /* Store an integer in the buffer */ void put(struct prodcons * b, int data) { pthread_mutex_lock(&b->lock); /* Wait until buffer is not full */ while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) { printf("wait for not full\n"); pthread_cond_wait(&b->notfull, &b->lock); } /* Write the data and advance write pointer */ b->buffer[b->writepos] = data; b->writepos++; if (b->writepos >= BUFFER_SIZE) b->writepos = 0; /* Signal that the buffer is now not empty */ pthread_cond_signal(&b->notempty); pthread_mutex_unlock(&b->lock); } /*--------------------------------------------------------*/ /* Read and remove an integer from the buffer */ int get(struct prodcons * b) { int data; pthread_mutex_lock(&b->lock); /* Wait until buffer is not empty */ while (b->writepos == b->readpos) { printf("wait for not empty\n"); pthread_cond_wait(&b->notempty, &b->
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

six2me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值