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

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

需求:使用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; 
}

运行结果:

 

 

 

 

 

 

目录
相关文章
|
2月前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
43 2
|
19天前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
20天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
14 1
|
25天前
|
监控 Java
在实际应用中选择线程异常捕获方法的考量
【10月更文挑战第15天】选择最适合的线程异常捕获方法需要综合考虑多种因素。没有一种方法是绝对最优的,需要根据具体情况进行权衡和选择。在实际应用中,还需要不断地实践和总结经验,以提高异常处理的效果和程序的稳定性。
19 3
|
29天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
42 4
|
29天前
|
数据采集 存储 Java
Crawler4j在多线程网页抓取中的应用
Crawler4j在多线程网页抓取中的应用
|
1月前
|
数据挖掘 程序员 调度
探索Python的并发编程:线程与进程的实战应用
【10月更文挑战第4天】 本文深入探讨了Python中实现并发编程的两种主要方式——线程和进程,通过对比分析它们的特点、适用场景以及在实际编程中的应用,为读者提供清晰的指导。同时,文章还介绍了一些高级并发模型如协程,并给出了性能优化的建议。
29 3
|
1月前
|
Java 数据处理 数据库
Java多线程的理解和应用场景
Java多线程的理解和应用场景
47 1
|
16天前
|
Java 开发者
Java中的多线程基础与应用
【10月更文挑战第24天】在Java的世界中,多线程是提高效率和实现并发处理的关键。本文将深入浅出地介绍如何在Java中创建和管理多线程,以及如何通过同步机制确保数据的安全性。我们将一起探索线程生命周期的奥秘,并通过实例学习如何优化多线程的性能。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往高效编程的大门。
15 0
|
1月前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
32 1