一、(LINUX 线程同步) 引入

原创水平有限有误请指出

线程相比进程有着先天的数据共享的优势,如下图,线程共享了进程除栈区以外的所有内存区域如下图所示:

但是这种共享有时候也会带来问题,简单的考虑如下C++代码:

点击(此处)折叠或打开

  1. /*************************************************************************
  2.   > File Name: error.cpp
  3.   > Author: gaopeng QQ:22389860 all right reserved
  4.   > Mail: gaopp_200217@163.com
  5.   > Created Time: Mon 15 May 2017 12:01:33 AM CST
  6.  ************************************************************************/
  7. #include<iostream>
  8. #include <pthread.h>
  9. #include <string.h>
  10. #define MAXOUT 1000000
  11. using namespace std;
  12. class testc
  13. {
  14.         private:
  15.                 int a;
  16.         public:
  17.                 testc()
  18.                 {
  19.                         a = 1;
  20.                 }
  21.                 testc& operator++()
  22.                 {
  23.                         int b = 0;
  24.                         b = a;
  25.                         a = b+1;
  26.                         return *this;
  27.                 }
  28.                 void prit()
  29.                 {
  30.                         cout<<a<<endl;
  31.                 }
  32. };
  33. testc test = test;
  34. void* testp(void* arg)
  35. {
  36.         int i = MAXOUT;
  37.         while(i--)
  38.         {
  39.                 ++test;
  40.                 cout<<pthread_self() <<":";
  41.                 test.prit();
  42.         }
  43. }
  44. int main(void)
  45. {
  46.         pthread_t tid[3];
  47.         int er;
  48.         int i = 0;
  49.         while(i<3)
  50.         {
  51.                 if ((er = pthread_create(tid+i,NULL,testp,NULL) )!=0 )
  52.                 {
  53.                         strerror(er);
  54.                         return -1;
  55.                 }
  56.                 i++;
  57.         }
  58.         i = 0;
  59.         while(i<3)
  60.         {
  61.                 pthread_join(*(tid+i),NULL);
  62.                 i++;
  63.         }
  64.         cout<<"last numer: ";
  65.         test.prit();
  66. }

实际上很简单我就是建立了一个全局变量,这个全局变量是全部线程共享,我开启了3个线程同时对里面的共享
数据进行更改,当
#define MAXOUT 100 
的时候这个时候最后输出如下:
last numer: 300
显然没有问题,但是如果我将
#define MAXOUT 1000000
增大到100W的时候最后输出为:
last numer: 2999972
我们明显的感觉到数据不对了,少了一些
出现这种问题在于,多个线程对同一个共享资源进行访问,因为线程是CPU调度的单位,而

点击(此处)折叠或打开

  1. {
  2.                         int b = 0;
  3.                         b = a;
  4.                         a = b+1;
  5.                         return *this;
  6.                 }

假设这个时候a 为 1000:
这段代码并不是原子性的,假设线程1正在修改
b = a;
这个时候时间片用完,线程2也执行这段代码,因为
a = b+1;并没有执行,所以共享区域的数据还没有变化
线程2拿到的数据任然是1000,也执行如下代码
b = a;
a = b+1;
这个时候线程2完成了修改,a为1001
当下一次线程1拿到CPU时间片,再次从就绪状转到执行态,接着
执行
a = b+1;
因为线程1中的b还是1000,所以a再次修改为1001,实际这个时候
是1002才对,线程2的修改被覆盖,出现上面的问题,通过语言的描述
也许不太明白,下面的时序图会帮助理解:

遇到这种问题当然就引入了我们的线程间同步的方法,常用的
1、互斥锁、条件变量
2、读写锁
3、信号量
4、spinlock
他们保护的是一段临界区代码,所谓临界区就是可能出现对同一个共享资源进行修改的代码,比如我这里

点击(此处)折叠或打开

  1. {
  2.                         int b = 0;
  3.                         b = a;
  4.                         a = b+1;
  5.                         return *this;
  6.                 }

就是临界区代码
后面将对他们进行描述,这里我们简单实用静态互斥锁进行解决这个问题。

点击(此处)折叠或打开

  1. //原子操作 加锁
  2.                 pthread_mutex_lock(&mtx);
  3.                 ++test;
  4.                 pthread_mutex_unlock(&mtx);
  5.                 //原子操作 解锁
  6.                 cout<<pthread_self() <<":";
  7.                 test.prit()

实际上我们就是保护了操作符重载的testc& operator++()
临界区的选择应该尽量小,避免对多线程的并发性产生较大的性能影响

具体代码如下:

点击(此处)折叠或打开

  1. /*************************************************************************
  2.   > File Name: error.cpp
  3.   > Author: gaopeng QQ:22389860 all right reserved
  4.   > Mail: gaopp_200217@163.com
  5.   > Created Time: Mon 15 May 2017 12:01:33 AM CST
  6.  ************************************************************************/
  7. #include<iostream>
  8. #include <pthread.h>
  9. #include <string.h>
  10. #define MAXOUT 1000000
  11. using namespace std;
  12. static pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;
  13. class testc
  14. {
  15.         private:
  16.                 int a;
  17.         public:
  18.                 testc()
  19.                 {
  20.                         a = 1;
  21.                 }
  22.                 testc& operator++()
  23.                 {
  24.                         int b = 0;
  25.                         b = a;
  26.                         a = b+1;
  27.                         return *this;
  28.                 }
  29.                 void prit()
  30.                 {
  31.                         cout<<a<<endl;
  32.                 }
  33. };
  34. testc test = test;
  35. void* testp(void* arg)
  36. {
  37.         int i = MAXOUT;
  38.         while(i--)
  39.         {
  40.                 //原子操作 加锁
  41.                 pthread_mutex_lock(&mtx);
  42.                 ++test;
  43.                 pthread_mutex_unlock(&mtx);
  44.                 //原子操作 解锁
  45.                 cout<<pthread_self() <<":";
  46.                 test.prit();
  47.         }
  48. }
  49. int main(void)
  50. {
  51.         pthread_t tid[3];
  52.         int er;
  53.         int i = 0;
  54.         while(i<3)
  55.         {
  56.                 if ((er = pthread_create(tid+i,NULL,testp,NULL) )!=0 )
  57.                 {
  58.                         strerror(er);
  59.                         return -1;
  60.                 }
  61.                 i++;
  62.         }
  63.         i = 0;
  64.         while(i<3)
  65.         {
  66.                 pthread_join(*(tid+i),NULL);
  67.                 i++;
  68.         }
  69.         cout<<"last numer: ";
  70.         test.prit();
  71. }

注意:一个简单类型的i++也不一定是一个原子操作,所以在涉及到并发修改共享变量的时候一定要使用
线程同步手段。

作者微信:

               

时间: 2024-09-20 00:08:40

一、(LINUX 线程同步) 引入的相关文章

二、(LINUX 线程同步) 互斥量、条件变量以及生产者消费者问题

原创转载请注明出处: 接上一篇: 一.(LINUX 线程同步) 引入  http://blog.itpub.net/7728585/viewspace-2137980/ 在线程同步中我们经常会使用到mutex互斥量,其作用用于保护一块临界区,避免多线程并发操作对这片临界区带来的数据混乱, POSIX的互斥量是一种建议锁,因为如果不使用互斥量也可以访问共享数据,但是可能是不安全的. 其原语包含: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

Linux线程同步之递归锁

概述 最常见的进程/线程的同步方法有互斥锁(或称互斥量Mutex),读写锁(rdlock),条件变量(cond),信号量(Semophore)等.在Windows系统中,临界区(Critical Section)和事件对象(Event)也是常用的同步方法. 简单的说,互斥锁保护了一个临界区,在这个临界区中,一次最多只能进入一个线程.如果有多个进程在同一个临界区内活动,就有可能产生竞态条件(race condition)导致错误. 读写锁从广义的逻辑上讲,也可以认为是一种共享版的互斥锁.如果对一个

Linux线程同步之信号C语言实例_C 语言

linux中向某个线程发送信号,若没有对该信号的处理函数,则会导致程序结束. 如下面的程序,创建一个线程,主线程向其发送一个信号,会导致程序立即结束 #include <stdio.h> #include <pthread.h> pthread_t t; void* run(void* arg) { while(1) { printf("Hello\n"); } } main() { pthread_create(&t, 0, run, 0); pthr

Linux线程同步之条件变量

条件变量是线程可用的另一种同步机制.条件变量给多个线程提供了一个会合的场所.条件本身是由互斥量保护的.线程在改变 条件状态前必须首先锁住互斥量. 条件变量的初始化 pthread_cond_init 去除初始化 pthread_cond_destroy 等待 pthread_cond_wait 满足条件给向进程发送信号 pthread_cond_signal 下面程序展示了利用条件变量等待另外两个线程满足条件时,第三个进程继续向前执行 #include <stdio.h> #include &

嵌入式 Linux线程同步读写锁rwlock示例

读写锁比mutex有更高的适用性,可以多个线程同时占用读模式的读写锁,但是只能一个线程占用写模式的读写锁.1. 当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞:2. 当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行枷锁的线程将阻塞:3. 当读写锁在读模式锁状态时,如果有另外线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求长期阻塞:这种锁适用对数据结构进行读

[转载]Linux 线程实现机制分析

  自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:兼容性.效率.本文从线程模型入手,通过分析目前 Linux 平台上最流行的 LinuxThreads 线程库的实现及其不足,描述了 Linux 社区是如何看待和解决兼容性和效率这两个问题的.   一.基础知识:线程和进程 按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位.在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持SMP以及减小(进程/线程)上下文切换开

Linux线程编程之生产者消费者问题

前言 本文基于顺序循环队列,给出Linux生产者/消费者问题的多线程示例,并讨论编程时需要注意的事项.文中涉及的代码运行环境如下: 本文假定读者已具备线程同步的基础知识. 一  顺序表循环队列 1.1 顺序循环队列定义 队列是一种运算受限的先进先出线性表,仅允许在队尾插入(入队),在队首删除(出队).新元素入队后成为新的队尾元素,元素出队后其后继元素就成为队首元素. 队列的顺序存储结构使用一个数组和两个整型变量实现,其结构如下: 1 struct Queue{ 2     ElemType el

银行存取款模型的线程同步问题

  关于线程同步,网上也有很多资料,不过不同的人理解也不大一样,最近在研究这个问题的时候回想起大学课本上的一个经典模型,即银行存取款模型,通过这个模型,我个人感觉解释起来还是比较清楚的.本文结合自己的思考对该模型进行一个简单的模拟,阐述一下我对线程同步的理解. 场景模拟   接下来使用java对该问题进行模拟.在研究这个问题时会忽略掉现实系统中的很多其他属性,通过一个最简单的余额问题来看线程同步,这里首先创建三个类. 1.卡类,同时卡类提供三个方法,获取余额.存款以及取款. public cla

关于dll与调用者线程同步的问题

问题描述 关于dll与调用者线程同步的问题 Dll导出函数创建了一个线程,该线程与该函数的调用者所在的线程如何同步? 解决方案 线程之间 的同步机制就那么几个,无论线程是否是从 DLL 导出的,使用的方法还是一样的,如:windows多线程--原子操作windows多线程同步--事件windows多线程同步--互斥量windows多线程同步--临界区windows多线程同步--信号量 Linux 下有类似的机制. 解决方案二: 为什么需要dll你的线程和外面的线程同步?程序模块的设计商是不是有问