一个简单案例理解为什么在多线程的应用中要使用锁

简介: 一个简单案例理解为什么在多线程的应用中要使用锁

需求:使用10个线程,同时对一个值count进行加一操作,每个线程对count加100000次,最终使得count=1000000

第一版代码:不加锁

lock.c

#include<stdio.h>
#include<pthread.h>
#define THREAD_COUNT 10
void *thread_callback(void *arg){
  int *pcount=(int*)arg;
  int i=0;
  while(i++ < 100000){
    (*pcount)++;
    usleep(1);//休眠一毫秒
  }
}
int main(){
  pthread_t threadid[THREAD_COUNT]={0};//定义多线程
  int i=0;
  int count=0;
  for(i=0;i<THREAD_COUNT;i++){
    pthread_create(&threadid[i],NULL,thread_callback,&count);
  }
    //每隔一秒打印一次count值
  for(i=0;i<100;i++){
    printf("count: %d\n",count);
    sleep(1);//休眠一秒
  }
  return 0; 
}

在Linux环境下编译执行:

编译:  gcc -o lock lock.c -pthread

执行: ./lock

运行结果:

在不加锁的情况之下,使用多线程无法满足需求,即count值无法加到1000000.

从代码分析唯一能改变count值得代码是:count++,源代码中为:(*pcount)++

count++转化为汇编语言为:

 

正常情况(预期情况):每一个线程的count++都执行完再执行另外一个线程的count++

eax:寄存器

 

不正常情况(实际情况):当前线程中count++没完全执行完就跳转到另外一个线程执行

 

线程在执行代码的过程中被打断,这样所带来的的结果就是两个线程中执行两次count++,但count实际值只加了1,使得结果不能达到我们所预期的1000000值.

解决方法:使用互斥锁,或自旋锁,或原子操作。

第二版代码:这里使用互斥锁(mutex)

#include<stdio.h>
#include<pthread.h>
#define THREAD_COUNT 10
pthread_mutex_t mutex;//定义一个锁
void *thread_callback(void *arg){
  int *pcount=(int*)arg;
  int i=0;
  while(i++ < 100000){
#if 0
    (*pcount)++;
#else
    pthread_mutex_lock(&mutex);//上锁
    (*pcount)++;
    pthread_mutex_unlock(&mutex);//解锁
#endif
    usleep(1);//休眠一毫秒
  }
}
int main(){
  pthread_t threadid[THREAD_COUNT]={0};//定义多线程
  pthread_mutex_init(&mutex,NULL);//初始化锁,NULL为系统默认
  int i=0;
  int count=0;
  for(i=0;i<THREAD_COUNT;i++){
    pthread_create(&threadid[i],NULL,thread_callback,&count);
  }
  for(i=0;i<100;i++){
    printf("count: %d\n",count);
    sleep(1);//休眠一秒
  }
  return 0; 
}

运行结果:

 

 

 

 

 

 

目录
相关文章
|
25天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
103 6
|
23天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
29 2
|
28天前
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
37 6
|
28天前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
28天前
|
存储 监控 安全
深入理解ThreadLocal:线程局部变量的机制与应用
在Java的多线程编程中,`ThreadLocal`变量提供了一种线程安全的解决方案,允许每个线程拥有自己的变量副本,从而避免了线程间的数据竞争。本文将深入探讨`ThreadLocal`的工作原理、使用方法以及在实际开发中的应用场景。
54 2
|
1月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
55 6
|
1月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
2月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
25 1
|
2月前
|
监控 Java
在实际应用中选择线程异常捕获方法的考量
【10月更文挑战第15天】选择最适合的线程异常捕获方法需要综合考虑多种因素。没有一种方法是绝对最优的,需要根据具体情况进行权衡和选择。在实际应用中,还需要不断地实践和总结经验,以提高异常处理的效果和程序的稳定性。
30 3