多线程代码问题描述

我们都知道,进程是操作系统对运行程序资源分配的基本单位,而线程是程序逻辑,调用的基本单位。在多线程的程序中,多个线程共享临界区资源,那么就会有问题:

比如

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> int g_val = ;
void * test1(void* args)
{
g_val = ;
printf("in %s: g_val = %d\n",__func__, g_val);
}
void * test2(void* args)
{
sleep();
printf("in %s: g_val = %d\n",__func__,g_val);
}
int main(int argc, char const *argv[])
{
pthread_t id1,id2;
pthread_create(&id1,NULL,test1,NULL);
pthread_create(&id2,NULL,test2,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return ;
}

由次我们可以看到,线程1修改了全局变量,而线程2中页跟着改变了。

那么,对于这个问题进行放大,我们就会找到多线程存在的问题。如下

#include <stdio.h>
#include <pthread.h>
// pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int g_val = ;
void* add(void *argv)
{
for(int i = ; i < ; ++i)
{
// g_val++;
// pthread_mutex_lock(&lock);
int tmp = g_val;
g_val = tmp+;
// pthread_mutex_unlock(&lock);
}
} int main(int argc, char const *argv[])
{
pthread_t id1,id2; pthread_create(&id1,NULL,add,NULL);
pthread_create(&id2,NULL,add,NULL); pthread_join(id1,NULL);
pthread_join(id2,NULL); printf("%d\n",g_val);
return ;
}

在上面代码中,我们执行两个线程分别对全局变量累加5000次,但是得到的结果却是不确定的。这是因为,在多线程程序中,线程调度使得线程间进行切换执行,如果当线程1将数据从内存读入cpu正在准备累加时,调度器切换线程2执行,此时,线程2获取的值是未累加的。那么,当两个线程都执行完本次累加后,实际值只增加了1。所以就会产生多次执行,结果不确定性。

注:代码中没有直接g_val++,而选择了tmp过度就是为了产生非原子操作,让调度过程处于累加未完时。

那么解决这个问题,就需要互斥操作了。

我们首先来谈互斥量mutex

通过互斥量实现线程锁,在每个线程累加之前,进行临界资源的锁操作,在结束时解锁,那么就能保证目标的实现了。

#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int g_val = ;
void* add(void *argv)
{
for(int i = ; i < ; ++i)
{
// g_val++;
pthread_mutex_lock(&lock);
int tmp = g_val;
g_val = tmp+;
pthread_mutex_unlock(&lock);
}
} int main(int argc, char const *argv[])
{
pthread_t id1,id2; pthread_create(&id1,NULL,add,NULL);
pthread_create(&id2,NULL,add,NULL); pthread_join(id1,NULL);
pthread_join(id2,NULL); printf("%d\n",g_val);
return ;
}

关于互斥锁的实现,在linux中实现如下

条件变量

问题场景描述

假设我们现在需要做一个生产者消费者模型,生产者对带有头节点的链表头插方式push_front生产数据,消费者调用pop_front消费数据.而生产者可能动作比较慢,这时就会有问题。

生产者生产一个数据时间,消费者可能迫切需求。因此,一直轮寻申请锁资源,以便进行消费。所以就会产生多次不必的锁资源申请释放动作。影响系统性能。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
typedef struct node
{
int _data;
struct node *_next;
}node_t,* node_p,**node_pp; node_p head = NULL; node_p alloc_node(int data)
{
node_p ret = (node_p)malloc(sizeof(node_t));
ret->_data = data;
ret->_next = NULL;
return ret;
} void init(node_pp phead)
{
*phead = alloc_node();
} void push_front(node_p head,int data)
{
node_p tmp = alloc_node(data);
tmp->_next = head->_next;
head->_next = tmp;
} void pop_front(node_p head, int * pdata)
{
if(head->_next!=NULL)
{
node_p tmp = head->_next;
head->_next = tmp->_next; *pdata = tmp->_data;
free(tmp);
}
} void show(node_p head)
{
node_p cur = head->_next;
while(cur)
{
printf("%d->", cur->_data);
cur = cur->_next;
}
printf("\n");
} //消费者
void * consumer(void *argv)
{
int data;
while()
{
pthread_mutex_lock(&lock);
// while(head->_next==NULL)
if(head->_next==NULL)
{
printf("producter is not ready\n");
// pthread_cond_wait(&cond,&lock);
// break;
}
else{
printf("producter is ready...\n");
pop_front(head,&data);
printf("%s data = %d \n",__func__, data);
}
pthread_mutex_unlock(&lock); sleep();
}
} void * producter(void * argv)
{
int data = rand()%;
while()
{
sleep();
pthread_mutex_lock(&lock);
push_front(head,data);
printf("%s data :: %d\n",__func__, data);
pthread_mutex_unlock(&lock);
// pthread_cond_signal(&cond);
}
} int main(int argc, char const *argv[])
{
init(&head); pthread_t id1,id2; pthread_create(&id1,NULL,consumer,NULL);
pthread_create(&id2,NULL,producter,NULL); pthread_join(id1,NULL);
pthread_join(id2,NULL);
}

由上,我们发现。生产者生叉一个数据之后,消费者总是会多次进行锁资源申请并尝试消费数据。那么,解决这一问题的方案就是:条件变量。

具体实现如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
typedef struct node
{
int _data;
struct node *_next;
}node_t,* node_p,**node_pp; node_p head = NULL; node_p alloc_node(int data)
{
node_p ret = (node_p)malloc(sizeof(node_t));
ret->_data = data;
ret->_next = NULL;
return ret;
} void init(node_pp phead)
{
*phead = alloc_node();
} void push_front(node_p head,int data)
{
node_p tmp = alloc_node(data);
tmp->_next = head->_next;
head->_next = tmp;
} void pop_front(node_p head, int * pdata)
{
if(head->_next!=NULL)
{
node_p tmp = head->_next;
head->_next = tmp->_next; *pdata = tmp->_data;
free(tmp);
}
} void show(node_p head)
{
node_p cur = head->_next;
while(cur)
{
printf("%d->", cur->_data);
cur = cur->_next;
}
printf("\n");
} //消费者
void * consumer(void *argv)
{
int data;
while()
{
pthread_mutex_lock(&lock);
while(head->_next==NULL)
// if(head->_next==NULL)
{
printf("producter is not ready\n\n");
pthread_cond_wait(&cond,&lock);
break;
}
// else{
// printf("producter is ready...\n");
pop_front(head,&data);
printf("%s data = %d \n",__func__, data);
// }
pthread_mutex_unlock(&lock); sleep();
}
} void * producter(void * argv)
{
int data = rand()%;
while()
{
sleep();
pthread_mutex_lock(&lock);
push_front(head,data);
printf("%s data :: %d\n",__func__, data);
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond); //条件变量v操作
}
} int main(int argc, char const *argv[])
{
init(&head); pthread_t id1,id2; pthread_create(&id1,NULL,consumer,NULL);
pthread_create(&id2,NULL,producter,NULL); pthread_join(id1,NULL);
pthread_join(id2,NULL);
}

由图可以看出,这下我们的消费者不再进行过多次没必要的轮寻访问,当生产者生产数据时,告诉消费者可以进行消费了,那么消费者进行消费。

其实这也就是著名的:好莱坞原则---不要打电话给我们,我们会通知你。

eg,在面试笔试中,我们不需要过度的紧张是否被录用,只需要在做到最大努力之后等着招聘方通知就好。

注:一个Condition Variable总是和一个Mutex搭配使用的。一个线程可以调用
pthread_cond_wait在一一个Condition Variable上阻塞等待,这个函数做以下三步操作:
1. 释放Mutex
2. 阻塞等待
3. 当被唤醒时,重新获得Mutex并返回

linux多线程-互斥&条件变量与同步的更多相关文章

  1. Linux多线程(三)(同步互斥)

    1. 线程的同步与互斥 1.1. 线程的互斥 在Posix Thread中定义了一套专门用于线程互斥的mutex函数.mutex是一种简单的加锁的方法来控制对共享资源的存取,这个互斥锁只有两种状态(上 ...

  2. Linux系统编程—条件变量

    条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用.条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等 ...

  3. 27 python 初学(信号量、条件变量、同步条件、队列)

    参考博客: www.cnblogs.com/yuanchenqi/articles/5733873.html  semaphore 信号量: condition 条件变量: event 同步条件:条件 ...

  4. OS: 读者写者问题(写者优先+LINUX+多线程+互斥量+代码)(转)

    一. 引子 最近想自己写个简单的 WEB SERVER ,为了先练练手,熟悉下在LINUX系统使用基本的进程.线程.互斥等,就拿以前学过的 OS 问题开开刀啦.记得当年学读者写者问题,尤其是写者优先的 ...

  5. linux中的条件变量

    1 大家可能知道互斥量是线程程序中必须的工具了,但是也不能是万能的,就比如某个线程正在等待共享数据某个条件的发生,这个时候会发生什么呢.它就可能重复的尝试对互斥对象锁定和解锁来检查共享数据结构. 2 ...

  6. POSIX多线程编程-条件变量pthread_cond_t

    条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用.使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化.一旦其它 ...

  7. 【Linux多线程】三个经典同步问题

    在了解了<同步与互斥的区别>之后,我们来看看几个经典的线程同步的例子.相信通过具体场景可以让我们学会分析和解决这类线程同步的问题,以便以后应用在实际的项目中. 一.生产者-消费者问题 问题 ...

  8. Linux Posix线程条件变量

    生产者消费者模型 .多个线程操作全局变量n,需要做成临界区(要加锁--线程锁或者信号量) .调用函数pthread_cond_wait(&g_cond,&g_mutex)让这个线程锁在 ...

  9. Linux 多线程互斥量互斥

    同步 同一个进程中的多个线程共享所在进程的内存资源,当多个线程在同一时刻同时访问同一种共享资源时,需要相互协调,以避免出现数据的不一致和覆盖等问题,线程之间的协调和通信的就叫做线程的同步问题, 线程同 ...

随机推荐

  1. Linux RPM 命令参数使用详解

    rpm 执行安装包二进制包(Binary)以及源代码包(Source)两种.二进制包可以直接安装在计算机中,而源代码包将会由 RPM自动编译.安装.源代码包经常以src.rpm作为后缀名. 常用命令组 ...

  2. mysql DB server端,如何让读写更快

    其实,我不是专业的DB管理同学,甚至算不上会了解.只是在最近的工作中,遇到了DB server端优化的契机,所以把这些手段记录下来: 通过调整这个参数的值,可以让DB更给力: 这两个参数的含义: 1. ...

  3. Android关于Theme.AppCompat相关问题的深入分析(转)

    http://www.jianshu.com/p/6ad7864e005e 先来看这样一个错误: No resource found that matches the given name '@sty ...

  4. ionic button笔记

    源码文件:_button.scss 和 _button-bar.scss,以及_variables.scss(66行-163行). 按钮是手机app不可或缺的一部分,不同风格的app,需要的按钮多种多 ...

  5. 动态密码卡TOTP算法

    TOTP NET实现:http://googleauthcsharp.codeplex.com/ 引用:http://www.cnblogs.com/wangxin201492/p/5030943.h ...

  6. TWaver家族新成员 — Legolas工业自动化设计平台

    对于TWaver可视化家族的成员,大家比较熟悉的是我们的网络拓扑图组件和MONO Design三维建模工具.作为开发工具,这两款产品面向广大的程序猿同志,在界面可视化上为大家省时省力.但是,当项目交付 ...

  7. MySQL慢日志监控脚本实例剖析

    公司线上的 MySQL 慢日志,之前一直没有做好监控.趁着上周空闲,我就把监控脚本写了下,今天特地把代码发出来与51博友分享一下. 针对脚本的注解和整体构思,我会放到脚本之后为大家详解. 1 2 3 ...

  8. JavaScript手札:《编写高质量JS代码的68个有效方法》(一)(1~5)

    编写高质量JS代码的68个有效方法(一) *:first-child { margin-top: 0 !important; } body>*:last-child { margin-botto ...

  9. [OpenCV] IplImage and Functions

    In this chapter, APIs will make U crazy. Good luck! Next, Review Linear Algebra.  Ref: http://blog.c ...

  10. Linux - Shell脚本调试方法

    Shell脚本调试选项 Shell本身提供一些调试方法选项: -n,读一遍脚本中的命令但不执行,用于检查脚本中的语法错误. -v,一边执行脚本,一边将执行过的脚本命令打印到标准输出. -x,提供跟踪执 ...