basename-freebsd使用std::string可以正常工作,但不使用
basename freebsd work correct with std::string, but without not
我必须对进行小程序
第一个
// compile with -lpthread
// TEST:
// basename
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <libgen.h>
#include <limits.h>
#include <inttypes.h>
// DATASET_LEN
#ifndef DATASET_LEN
#define DATASET_LEN 10000
#endif
// THREADS_NUM
#ifndef THREADS_NUM
#define THREADS_NUM 16
#endif
// need to call free(3) after
char** generateArray() {
char** dataset = (char**)malloc(sizeof(char*) * DATASET_LEN);
// fill dataset
for (size_t i = 0; i < DATASET_LEN; ++i) {
dataset[i] = (char*)malloc(sizeof(char) * CHAR_MAX);
sprintf(dataset[i], "%i/%i/", rand(), rand());
}
return dataset;
}
// pthread_create(3) callback
void* run(void* args) {
char** dataset = generateArray();
char* baseName;
for (size_t i = 0; i < DATASET_LEN; ++i) {
baseName = basename(dataset[i]);
printf("%sn", baseName);
free(dataset[i]);
}
free(dataset);
}
// main
int main(int argc, char** argv) {
pthread_t* threads = (pthread_t*)malloc(sizeof(pthread_t) * THREADS_NUM);
// threads start
for (int i = 1; i <= THREADS_NUM; ++i) {
pthread_create(&threads[i-1], NULL, run, NULL);
fprintf(stderr, "Thread %u startedn", i);
}
// threads join
for (int i = 1; i <= THREADS_NUM; ++i) {
pthread_join(threads[i-1], NULL);
fprintf(stderr, "Thread %u finishedn", i);
}
free(threads);
return EXIT_SUCCESS;
}
第二:
// compile with -lpthread
// TEST:
// basename
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <libgen.h>
#include <limits.h>
#include <inttypes.h>
#include <string>
// DATASET_LEN
#ifndef DATASET_LEN
#define DATASET_LEN 10000
#endif
// THREADS_NUM
#ifndef THREADS_NUM
#define THREADS_NUM 16
#endif
// need to call free(3) after
char** generateArray() {
char** dataset = (char**)malloc(sizeof(char*) * DATASET_LEN);
// fill dataset
for (size_t i = 0; i < DATASET_LEN; ++i) {
dataset[i] = (char*)malloc(sizeof(char) * CHAR_MAX);
sprintf(dataset[i], "%i/%i/", rand(), rand());
}
return dataset;
}
// pthread_create(3) callback
void* run(void* args) {
char** dataset = generateArray();
char* baseName;
std::string tmpStr;
for (size_t i = 0; i < DATASET_LEN; ++i) {
baseName = basename(dataset[i]);
tmpStr = std::string(baseName);
printf("%sn", tmpStr.c_str());
free(dataset[i]);
}
free(dataset);
}
// main
int main(int argc, char** argv) {
pthread_t* threads = (pthread_t*)malloc(sizeof(pthread_t) * THREADS_NUM);
// threads start
for (int i = 1; i <= THREADS_NUM; ++i) {
pthread_create(&threads[i-1], NULL, run, NULL);
fprintf(stderr, "Thread %u startedn", i);
}
// threads join
for (int i = 1; i <= THREADS_NUM; ++i) {
pthread_join(threads[i-1], NULL);
fprintf(stderr, "Thread %u finishedn", i);
}
free(threads);
return EXIT_SUCCESS;
}
这两个程序在linux下都能正常工作,但首先在freebsd上(没有std::string)不能工作
有人能解释为什么吗?
我在/usr/src/lib/libc/gen/basename.c
中看到了freebsd-src,并在函数中看到了一个静态var
但正因为如此,用std::string程序也不能正常工作
正常情况下,我的意思是,它只输出数字和新的行
对于我使用的测试:./freebsd-threaded-basename | egrep -av '^[0-9ns]+$' | env LANG=c less
UPD我尝试使用strdup()或strcpy()结果相同-不正常
UPD*每次运行带有std::string的版本时,它都会按预期运行
程序行为不可预测的原因是basename
,它不是线程安全的。basename
有点过时了。现代C++应用程序倾向于使用其他方法来解析文件路径。Boost文件系统库很受欢迎,可以用来做这件事。
如果您坚持使用basename
,请将其与一些代码一起放在关键部分,这些代码将获得basename
的结果(无论是printf
、strcpy
还是其他代码)。这保证了basename
的结果不会同时从多个线程访问。这意味着正确的行为。
现在有一些关于"为什么"的猜测。(由于无法预测非线程安全的多线程程序究竟是如何工作的,所以只能猜测)。
程序的第一个版本部分并行执行basename
循环(basename
函数和循环本身),部分按顺序执行(printf
和free
是线程安全函数,它们的实现受到关键部分的保护)。
第二个版本添加了std::string
,这意味着更多的顺序代码。它为一个新字符串分配内存,释放旧内存(这两种操作都是线程安全的,并受到关键部分的保护)。此外(在某些实现中)is使用原子操作来更新共享计数器,这也降低了并行性。所有这些实际上都将您的程序从并行转换为完全顺序。所有线程大多都在等待某个互斥对象。或者有时执行一些复杂的printf/memory/std::字符串计算。并且很少有一个线程进行相对简单的CCD_ 15计算。几乎就像你在basename
周围添加了一个关键部分。
Linux测试的正确结果可能是因为printf
和free
足以使程序在这种情况下几乎按顺序运行。(因为Linux中的某些操作方式不同,或者硬件不同)。
从pthreads:上的Linux手册页面
POSIX.1-2001和POSIX.1-2008要求标准中规定的所有功能都应是线程安全的,但以下功能除外:
【功能列表】
basename()
因此,不能保证basename
是线程安全的(尽管有些实现可能会这样做)。如果你想让你的应用程序是可移植的,你必须用互斥锁之类的东西来保护调用。
另请参阅POSIX参考,其中明确表示:
basename()函数可以修改路径指向的字符串,并可以返回一个指向静态存储的指针,该指针随后可能会被对basename(()的调用覆盖。
basename()函数不需要是线程安全的。
这在FreeBSD上basename()的手册页中有解释,您可以在这里找到:
http://www.freebsd.org/cgi/man.cgi?query=basename&sektion=3
特别是:
实施说明basename()函数返回一个指向内部存储空间allo的指针-在将被后续调用覆盖的第一个调用上指定。因此,basename_r()是线程应用程序的首选项。
因此,从basename()返回的数据可能已被正在使用的其他线程覆盖。使用basename_r可以防止这种情况。
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- <<操作员在下面的行中工作
- 有人能解释一下为什么下界是这样工作的吗C++的
- ExtractIconEx:可以工作,但偶尔会崩溃
- C++中的memset函数工作不正常
- 为什么STD ::计数将常数传递给Lambda,而不是在弦上工作时而不是字符
- sdl软件渲染器不工作,工作在硬件加速的一个
- C++程序已停止工作-求解常微分方程