您的位置:首页 > 运维架构 > Linux

linux线程3-互斥与同步

2017-05-30 02:45 676 查看

简介

多线程共享一个进程的地址空间,多线程间通信容易进行,但是多线程同时访问共享对象时需要引入同步和互斥机制.

同一个进程的线程共享进程内的绝大部分资源,当一段访问这些共享资源的代码块,有可能被多个线程执行,那么这块代码就被叫做临界区.

当有多个线程并发的在临界区执行时,程序的执行结果会出现不确定性,这种情况被叫做静态条件.

一个demo代码

//
// Created by wuxiao on 17-5-29.
//

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>

#ifndef handle_error_en
#define handle_error_en(en,msg)\
errno = en;perror(msg);exit(EXIT_FAILURE);
#endif
//变量名等规则这里先不考虑
//多线程共享资源
static int global = 0;

static void *thread_routine(void *arg)
{
int loc,j;
for(j = 0; j < 10000000;j++)
{
loc = global;
loc++;
global = loc;
}
return nullptr;
}

int main(int argc,char* argv[])
{
pthread_t t1,t2;
int result;

//创建两个线程,并修改共享资源global
result = pthread_create(&t1,NULL,thread_routine,NULL);
result += pthread_create(&t2,NULL,thread_routine,NULL);
if(result != 0)
{
handle_error_en(result,"pthread_create");
}

//等待子线程退出
result = pthread_join(t1, nullptr);
result += pthread_join(t2, nullptr);
if(result != 0)
{
handle_error_en(result,"pthread_join");
}
printf("global = %d\n",global);
exit(EXIT_SUCCESS);
}




- 发现共享变量不是预期值,原因就是进入了静态条件,需要保证多个线程在临界区时互斥的,常用方法是加互斥锁

- 锁分静态和动态:

/ 静态初始化一个全局的互斥锁
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;


#include <pthread.h>
// 动态分配一个互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
// 释放动态分配的互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);


加锁和解锁:

#include <pthread.h>
// 持有互斥锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
// 释放互斥锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);


加锁后的demo代码

//
// Created by wuxiao on 17-5-29.
//

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>

#ifndef handle_error_en
#define handle_error_en(en,msg)\
errno = en;perror(msg);exit(EXIT_FAILURE);
#endif
//变量名等规则这里先不考虑
//多线程共享资源
static int global = 0;
//静态锁
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

static void *thread_routine(void *arg)
{
int loc,j;
for(j = 0; j < 10000000;j++)
{
//加锁
pthread_mutex_lock(&mtx);
loc = global;
loc++;
global = loc;
//解锁
pthread_mutex_unlock(&mtx);
}
return nullptr;
}

int main(int argc,char* argv[])
{
pthread_t t1,t2;
int result;

//创建两个线程,并修改共享资源global
result = pthread_create(&t1,NULL,thread_routine,NULL);
result += pthread_create(&t2,NULL,thread_routine,NULL);
if(result != 0)
{
handle_error_en(result,"pthread_create");
}

//等待子线程退出
result = pthread_join(t1, nullptr);
result += pthread_join(t2, nullptr);
if(result != 0)
{
handle_error_en(result,"pthread_join");
}
printf("global = %d\n",global);
exit(EXIT_SUCCESS);
}




线程同步

加解锁一般是为了解决竞态条件的问题,但是有时候我们需要让线程按序执行,
4000
这种确保多线程间按照先后顺序执行的技术,就叫做线程同步.

条件变量是线程同步的主要手段,例如线程B调用条件变量让接口阻塞,线程A在处理完资源后,通过条件变量接口来环境等待资源的线程B(有点像中断的感觉..)

条件变量初始化分静态和动态

// 与互斥锁类似静态初始化一个全局的条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;


//动态
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);


通知和等待条件变量

include <pthread.h>
// 等待一个指定的条件变量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
// 唤醒一个等待该条件变量的线程
int pthread_cond_signal(pthread_cond_t *cond);
// 唤醒所有等待该条件变量的线程
int pthread_cond_broadcast(pthread_cond_t *cond);


demo代码:

//
// Created by wuxiao on 17-5-29.
//

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>

//硬编码,命名等问题先不管
pthread_t threads[2];
char writer_char[2] = {'A','B'};
//锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//全局写入次数变量
#define MAX_WRITE_TIMES 10
int  write_times = 0;

struct file_res
{
//当前文件写入的线程
pthread_t *writer;
//文件描述符
int fd;
}file_res =
{
.writer = &threads[0],
};

//两个线程的入口函数
void* writer_routine(void *arg)
{
int index = (intptr_t)arg;
size_t i = 0;
int next_index = 0;
printf("thread %d is running,will write %c\n",index,writer_char[index]);

while(1)
{
//如果已经到达最大写入次数
if(write_times >= MAX_WRITE_TIMES)
{
//结束当前线程
pthread_exit(NULL);
}
if(pthread_mutex_lock(&mutex) != 0)
{
exit(EXIT_FAILURE);
}

for(;;)
{
//如果当前线程可写入文件,写文件
if(&threads[index] == file_res.writer)
{
write(file_res.fd,&writer_char[index],sizeof(writer_char[index]));

//更新文件的写入线程
next_index = (index + 1)%2;
file_res.writer = &threads[next_index];
write_times++;
//跳出死循环,通知下一个线程进场
break;
}
//当前线程不可写,等待下一个线程唤醒
pthread_cond_wait(&cond,&mutex);

}

//解锁并唤醒下一个线程
if(pthread_mutex_unlock(&mutex) != 0)
{
exit(EXIT_FAILURE);
}
pthread_cond_signal(&cond);
}

}

int main(int argc,char* argv[])
{
//创建文件
char file_name[] = "file";
if(file_res.fd = open(file_name,O_RDWR|O_CREAT|O_TRUNC,0666) < 0)
{
printf("open %s fail\n",file_name);
exit(EXIT_FAILURE);
}

//创建线程1和2
size_t i;
for(i = 0;i < sizeof(threads)/sizeof(pthread_t);i++)
{
if(pthread_create(&threads[i],NULL,writer_routine,(void*)(intptr_t)i) != 0)
{
printf("create thread %zu fail\n", i);
exit(EXIT_FAILURE);
}
}

pthread_exit(NULL);

}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 线程 多线程