You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

网页计数器值低于访问次数问题及模拟程序解析

网页计数器数值低于实际访问次数的原因分析及C语言模拟程序解读

一、为什么计数器会“漏记”访问次数?

最核心的原因就是竞态条件(Race Condition)——当多个并发线程(或进程)同时操作共享的计数器变量时,因为修改操作不是原子性的,就会导致数据丢失。具体来说:

  • 非原子的读写操作:对计数器的更新是“读取当前值→加1→写回新值”三个独立步骤,不是一次性完成的原子操作。如果两个线程同时读取到同一个旧值,各自加1后写回,结果就相当于只统计了一次访问,白白丢了一次计数。
  • 缺少同步保护:没有用互斥锁、信号量这类同步机制来锁定计数器的访问,多个线程可以随意穿插修改操作,完全没有秩序。
  • 多CPU缓存的影响:在多核CPU环境下,每个核心可能会把计数器缓存到自己的本地缓存里,线程读取的是缓存中的旧值,写入后其他核心的缓存没有及时同步,也会导致后续线程读到错误的数值。

二、解读这段C语言模拟程序

先把完整的程序补全(你给出的代码有截断,我补全了核心逻辑):

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
// 重复100次以模拟100次随机页面访问
#define RPT 100
// 网页访问计数器
int cnt=0;
void* counter() {
 int cntLocalCopy;
 float r;
 cntLocalCopy = cnt;
 // 模拟服务器向浏览器提供页面的耗时工作
 for(int i=0; i<10000; i++) {
     r = i * 1.23; // 用无意义的计算模拟处理请求的耗时
 }
 cnt = cntLocalCopy + 1;
 return NULL;
}

int main() {
 pthread_t threads[RPT];
 // 创建100个线程,每个线程模拟一次页面访问
 for(int i=0; i<RPT; i++) {
     pthread_create(&threads[i], NULL, counter, NULL);
 }
 // 等待所有线程执行完毕
 for(int i=0; i<RPT; i++) {
     pthread_join(threads[i], NULL);
 }
 printf("最终计数器值:%d\n", cnt);
 return 0;
}

这段程序精准模拟了网页计数器漏记的场景,关键点如下:

  • 全局共享变量无保护:cnt是全局变量,所有100个线程都能直接读写,没有任何锁来限制访问顺序。
  • 线程的执行逻辑放大了竞态条件:每个线程先把cnt的值读到本地变量cntLocalCopy,然后去执行一段耗时的模拟工作(那段循环)。这段耗时操作故意拉长了“读取旧值”和“写回新值”之间的时间窗口,让多个线程更容易同时处于这个窗口中——比如线程A刚读完cnt=10就去忙计算了,这时候线程B、C也跟着读到cnt=10,等它们都忙完后,都会把11写回cnt,结果三次访问只记了一次。
  • 最终结果必然小于100:运行这个程序,你会发现输出的计数器值大概率远小于100,这就是竞态条件导致的计数丢失,和实际网页服务器中遇到的问题完全一致。

如果要修复这个问题,只需要给计数器的访问加个互斥锁:

// 新增互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* counter() {
 int cntLocalCopy;
 float r;
 // 加锁,确保同一时间只有一个线程能访问cnt
 pthread_mutex_lock(&mutex);
 cntLocalCopy = cnt;
 cnt = cntLocalCopy + 1;
 pthread_mutex_unlock(&mutex);
 // 模拟工作放在锁外面,避免影响并发性能
 for(int i=0; i<10000; i++) {
     r = i * 1.23;
 }
 return NULL;
}

这样就能保证“读取-修改-写入”是原子操作,最终计数器值会准确等于100。

内容的提问来源于stack exchange,提问作者Ibo Cetinkaya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:06:13