basename freebsd使用std :: string工作正确,但没有没有

时间:2012-01-30 08:24:08

标签: c++ string freebsd

我需要小程序

第一

// 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("%s\n", 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 started\n", i);
    }
    // threads join
    for (int i = 1; i <= THREADS_NUM; ++i) {
        pthread_join(threads[i-1], NULL);
        fprintf(stderr, "Thread %u finished\n", 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("%s\n", 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 started\n", i);
    }
    // threads join
    for (int i = 1; i <= THREADS_NUM; ++i) {
        pthread_join(threads[i-1], NULL);
        fprintf(stderr, "Thread %u finished\n", 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-9\n\s]+$' | env LANG=c less

UPD 我尝试使用strdup()或strcpy()结果是一样的 - 不正常
UPD * 每个 *时间运行std :: string的版本按预期工作

3 个答案:

答案 0 :(得分:2)

程序无法预测的原因是basename,这不是线程安全的。 basename有点过时了。现代C ++应用程序倾向于使用其他方法来解析文件路径。 Boost Filesystem Library很受欢迎,可能会用来做它。

如果您坚持使用basename,请将其与一些代码一起放入关键部分,这些代码将获得basename的结果(printfstrcpy,或其他一些)。这可以保证不会同时从多个线程访问basename的结果。这意味着正确的行为。

现在有些猜测“为什么”。 (只是猜测,因为无法预测,非线程安全的多线程程序是如何工作的。)

程序的第一个版本执行basename部分并行循环(basename函数和循环本身),部分顺序执行(printffree是线程安全函数,他们的实施受到关键部分的保护。)

第二个版本添加std::string,这意味着更多的顺序代码。它为新字符串分配内存,释放旧内存(这些操作都是线程安全的,并受关键部分保护)。此外(在一些实现中)使用原子操作来更新共享计数器,这也降低了并行性。所有这些实际上将您的程序从并行转换为完全顺序。所有线程大多等待一些互斥锁。或者有时执行一些复杂的printf / memory / std :: string计算。并且很少有一个线程执行相对简单的basename计算。几乎就像你在basename附近添加了一个关键部分。

Linux测试的正确结果可能是因为printffree足以使程序在这种情况下几乎是顺序的。 (因为在Linux中或者由于硬件不同,某些事情会有所不同)。

答案 1 :(得分:1)

来自Linux manual page on pthreads

  

POSIX.1-2001和POSIX.1-2008要求标准中指定的所有函数都是线程安全的,但以下函数除外:

     

[功能列表]

basename()

因此basename不保证是线程安全的(尽管某些实现可能会这样做)。如果您希望应用程序可移植,则必须使用互斥锁等方式保护呼叫。

另请参阅明确说明的POSIX参考:

  

basename()函数可以修改path指向的字符串,并且可以返回指向静态存储的指针,然后可以通过对basename()的后续调用来覆盖该指针。

     

basename()函数不必是线程安全的。

答案 2 :(得分:1)

FreeBSD上basename()的手册页对此进行了解释,您可以在此处找到:

http://www.freebsd.org/cgi/man.cgi?query=basename&sektion=3

特别是:

  

实施说明        basename()函数返回指向内部存储空间的指针        在第一次通话时输入,后续通话将覆盖该通话。        因此,basename_r()是线程应用程序的首选。

因此,从basename()返回的数据可能已被您正在使用的其他线程覆盖。使用basename_r可以防止这种情况。