多线程程序中计数的意外增加
unnexpected increment of count in multithreaded program
我正在尝试 运行 每个线程的给定卷发数。 pull_one_url()
是在 pthread_create()
中从 main()
调用的函数。 cnt_limit
是一个全局变量,在main中设置,在threads中只读。
我有时看到的意外行为是局部计数变量增加到高值。
对于给定的 运行 线程数为 10,cnt_limit
(每个线程的卷曲数)设置为 10。
命令行 运行:./a.out 10 10 10.140.71.12
意外的输出是:
10 2682
10 2858
10 2804
10 2988
10 2871
10 2940
10 2864
10 2609
10 2816
10 2893
预期输出:对于给定的输入,每 10 个线程需要执行 10 个 curl 请求。
行数 = 线程数
每行中的第一个数字 = 每个线程需要执行的 curl 请求数。
每行中的第二个数字 = 每个线程执行的 curl 请求数。
10 10
10 10
10 10
10 10
10 10
10 10
10 10
10 10
10 10
10 10
源代码:
#include <stdio.h>
#include <pthread.h>
#include <curl/curl.h>
#include <stdlib.h>
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
// printf("got response\n");
return size * nmemb;
}
int cnt_limit;
static void *pull_one_url(void *url)
{
CURL *curl;
int cnt;
int count = 0;
for (cnt=0; cnt < cnt_limit; cnt++) {
CURLcode res;
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
res = curl_easy_perform(curl);
int response_code;
if (res == CURLE_OK) {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
//printf("%d\n", response_code);
}
else {
//printf("400\n");
}
curl_easy_cleanup(curl);
count++;
//printf("%d\n", cnt);
}
printf("%d %d\n", cnt_limit, count);
return NULL;
}
int main(int argc, char **argv)
{
int n = atoi(argv[1]);
cnt_limit=atoi(argv[2]);
pthread_t tid[n];
int i;
curl_global_init(CURL_GLOBAL_ALL);
for(i = 0; i< n; i++) {
int error = pthread_create(&tid[i],
NULL,
pull_one_url,
(void *) argv[3]);
if(0 != error)
fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error);
//else
//fprintf(stderr, "Thread %d, gets %s\n", i, argv[2]);
}
/* now wait for all threads to terminate */
for(i = 0; i< n; i++) {
pthread_join(tid[i], NULL);
//fprintf(stderr, "Thread %d terminated\n", i);
}
return 0;
}
编译:gcc source_code.c -lpthread -lcurl
运行 : ./a.out 10 10 <url>
已编辑:在注释掉循环内的 libcurl 代码后,我还没有观察到这种行为。但之前观察到的意外行为也不是很一致。问题仍然存在,这个 libcurl 代码如何影响循环中的简单计数器。
在花了很多时间试图理解这一点之后,我将其张贴在这里。我想我犯了一些愚蠢的错误。
任何帮助将不胜感激。
根据评论区的讨论结果,问题似乎是:
OP 发布的部分代码如下:
int response_code;
[...]
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
根据curl documentation on CURLINFO_RESPONSE_CODE,最后一个参数的类型是long *
。但是,OP 正在传递 int *
类型的参数。由于 OP 使用 sizeof(long) == 8
而 sizeof(int) == 4
的平台,curl 库将 8 个字节写入 response_code
的地址,尽管在堆栈上只为该变量分配了 4 个字节.这可能导致堆栈上的相邻变量被覆盖。
要解决此问题,行
int response_code;
应改为:
long response_code;
我正在尝试 运行 每个线程的给定卷发数。 pull_one_url()
是在 pthread_create()
中从 main()
调用的函数。 cnt_limit
是一个全局变量,在main中设置,在threads中只读。
我有时看到的意外行为是局部计数变量增加到高值。
对于给定的 运行 线程数为 10,cnt_limit
(每个线程的卷曲数)设置为 10。
命令行 运行:./a.out 10 10 10.140.71.12
意外的输出是:
10 2682
10 2858
10 2804
10 2988
10 2871
10 2940
10 2864
10 2609
10 2816
10 2893
预期输出:对于给定的输入,每 10 个线程需要执行 10 个 curl 请求。
行数 = 线程数
每行中的第一个数字 = 每个线程需要执行的 curl 请求数。
每行中的第二个数字 = 每个线程执行的 curl 请求数。
10 10
10 10
10 10
10 10
10 10
10 10
10 10
10 10
10 10
10 10
源代码:
#include <stdio.h>
#include <pthread.h>
#include <curl/curl.h>
#include <stdlib.h>
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
// printf("got response\n");
return size * nmemb;
}
int cnt_limit;
static void *pull_one_url(void *url)
{
CURL *curl;
int cnt;
int count = 0;
for (cnt=0; cnt < cnt_limit; cnt++) {
CURLcode res;
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
res = curl_easy_perform(curl);
int response_code;
if (res == CURLE_OK) {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
//printf("%d\n", response_code);
}
else {
//printf("400\n");
}
curl_easy_cleanup(curl);
count++;
//printf("%d\n", cnt);
}
printf("%d %d\n", cnt_limit, count);
return NULL;
}
int main(int argc, char **argv)
{
int n = atoi(argv[1]);
cnt_limit=atoi(argv[2]);
pthread_t tid[n];
int i;
curl_global_init(CURL_GLOBAL_ALL);
for(i = 0; i< n; i++) {
int error = pthread_create(&tid[i],
NULL,
pull_one_url,
(void *) argv[3]);
if(0 != error)
fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error);
//else
//fprintf(stderr, "Thread %d, gets %s\n", i, argv[2]);
}
/* now wait for all threads to terminate */
for(i = 0; i< n; i++) {
pthread_join(tid[i], NULL);
//fprintf(stderr, "Thread %d terminated\n", i);
}
return 0;
}
编译:gcc source_code.c -lpthread -lcurl
运行 : ./a.out 10 10 <url>
已编辑:在注释掉循环内的 libcurl 代码后,我还没有观察到这种行为。但之前观察到的意外行为也不是很一致。问题仍然存在,这个 libcurl 代码如何影响循环中的简单计数器。
在花了很多时间试图理解这一点之后,我将其张贴在这里。我想我犯了一些愚蠢的错误。 任何帮助将不胜感激。
根据评论区的讨论结果,问题似乎是:
OP 发布的部分代码如下:
int response_code;
[...]
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
根据curl documentation on CURLINFO_RESPONSE_CODE,最后一个参数的类型是long *
。但是,OP 正在传递 int *
类型的参数。由于 OP 使用 sizeof(long) == 8
而 sizeof(int) == 4
的平台,curl 库将 8 个字节写入 response_code
的地址,尽管在堆栈上只为该变量分配了 4 个字节.这可能导致堆栈上的相邻变量被覆盖。
要解决此问题,行
int response_code;
应改为:
long response_code;