【问题标题】:Implementing mutexes for file writes为文件写入实现互斥锁
【发布时间】:2018-03-07 15:23:39
【问题描述】:

我正在尝试使用互斥锁来避免多次写入 C/Cpp 中的同一线程。下面是我的程序流程。我对在哪里包含我的锁定和解锁代码感到困惑。

main() {
    spawn a worker thread
}
worker_thread() {
    read the input file name 
    read some content
    write the content to the given file name
}

我看到的大多数实现,似乎都有这样的东西:

main() {
    pthread_mutex_init(&myMutex;,0);
    *spawn a worker thread*
    pthread_join(thread1, 0);
    pthread_mutex_destroy(&myMutex;);
}
worker_thread() {
    read the input file name 
    read some content
    write the content to the given file name
}

我想要的是这样的:

main() {
    spawn a worker thread
}
worker_thread() {
    read the input file name 
    read some content
    pthread_mutex_init(&myMutex;,0) --> for the given file?
    write the content to the given file name
    pthread_mutex_destroy(&myMutex;);
}

非常感谢任何想法。谢谢!

【问题讨论】:

  • 你使用的是c还是c++?如果您使用 c++11 及更高版本,请不要使用 pthread_mutex 而是使用 std::mutex
  • 您是否试图防止在两个不同的线程中写入同一个文件?
  • 您需要决定要保护哪些对象免受哪些线程或代码路径的并发访问。如果您只在一个线程中创建和使用了某些东西,那么除非您主动与另一个线程共享它,否则无需保护对该对象的访问。
  • 为什么您试图阻止同时写入您的文件?根据您的操作系统,there are ways to do atomic write operations to a file without locking at the application level
  • 是的,我正在尝试防止不同线程同时写入同一个文件。

标签: c++ c multithreading synchronization mutex


【解决方案1】:

为 iostream 创建一个包装器相当容易,以确保一次只有一个线程可以写入流。不幸的是,几乎一旦你这样做,你就会遇到另一个问题。它确保一次只有一个线程可以插入到流中,因此您可以获得定义的行为。但是,如果您有类似的情况:

线程 1:sync_stream << a << b << c << '\n';
线程2:sync_stream << x << y << z << '\n';

你想要的是:

abc
xyz

...否则:

xyz
abc

由于它们位于不同的线程中,因此它们之间的顺序可以不同,但​​一个线程的一行输出应保持单行输出。比如:

abxy
cz

...可能不受欢迎或不可接受。为了避免这种情况,我们确实需要两个单独的类。一种是同步流。另一个是让我们将一些(或多或少任意的)插入流作为一个单一的、不可分割的“事务”。为此,我们可以使用这样的一对类:

class transaction {
    std::ostringstream buffer;
public:
    transaction(std::string const &s="") : buffer(s, std::ios::out | std::ios::ate) {}

    template <class T>
    transaction &operator<<(T const &t) {
        buffer << t;
        return *this;
    }

    friend std::ostream &operator<<(std::ostream &os, transaction const &t) {
        return os << t.buffer.str();
    }
};

class sync_stream {
    std::ostream &out;
    std::mutex mutex;
public:
    sync_stream(std::ostream &sink) : out(sink) { }

    void operator<<(transaction const &t) {
        std::lock_guard<std::mutex> l(mutex);
        out << t;
    }    
};

请注意transaction 类支持链接,但sync_stream 不支持(您可以插入其中的唯一内容是transaction)。要使用它们,我们执行以下操作:

for (int i=0; i<10; i++)
    threads[i] = std::thread([&]{ 
        for (int i=0; i<10; i++) 
            s << (transaction() << "Thread: " << std::this_thread::get_id() << "\n");
    });

这样,线程认为的单个输出实际上是单个输出,所以我们的结果可能如下所示:

主题:140375947724544
主题:140376068564736
主题:140375964509952
主题:140375964509952
主题:140375972902656
主题:140375964509952

当然,你会得到与我不同的线程 ID,并且行的顺序可能会有所不同——但每一行都会被写成一个完整的单元。

总结

工作线程根本不应该直接使用互斥锁。这应该是自动化的,因此工作线程可以专注于其工作,并且只需在完成其工作所需的底层机制上花费最少的精力。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2011-07-20
    • 2010-12-01
    相关资源
    最近更新 更多