【操作系统】常见多线程编程问题梳理

107 阅读5分钟

42106779_p0jpg

GitHub上的代码下载链接:github.com/WU-SUNFLOWE…

两个线程轮流打印奇偶数

#include <thread>
#include <mutex>
#include <condition_variable>

int counter = 1;
int max_counter = 100;

std::mutex mtx;
std::condition_variable cv;

void PrintOddWorker() {
    while (true) {
        std::unique_lock<std::mutex> locker(mtx);
        cv.wait(locker, []() {
            return counter % 2 == 1 || counter > max_counter;
        });
        if (counter > max_counter) {
            break;
        }
        printf("PrintOddWorker: %d\n", counter);
        
        ++counter;
        cv.notify_one();
    }
}

void PrintEvenWorker() {
    while (true) {
        std::unique_lock<std::mutex> locker(mtx);
        cv.wait(locker, []() {
            return counter % 2 == 0 || counter > max_counter;
        });
        if (counter > max_counter) {
            break;
        }
        printf("PrintEvenWorker: %d\n", counter);

        ++counter;
        cv.notify_one();
    }
}

int main() {
    std::thread t1(&PrintEvenWorker);
    std::thread t2(&PrintOddWorker);

    t1.join();
    t2.join();

    printf("Main Thread Finished!\n");
}

N个线程轮流打印

#include <mutex>
#include <thread>
#include <vector>
#include <iostream>
#include <condition_variable>

int counter = 0;
const int max_counter = 50;

std::mutex mtx;
std::condition_variable cv;

void DoWork(int thread_id, int total_threads) {
    while (true) {
        std::unique_lock<std::mutex> locker(mtx);
        {
            cv.wait(locker, [total_threads, thread_id]() {
                return counter % total_threads == thread_id || counter > max_counter;
            });

            if (counter > max_counter) {
                break;
            }

            printf("Thread(%d) : %d\n", thread_id, counter);

            ++counter;            
        }
        locker.unlock();

        cv.notify_all();  // DON'T USE notify_one !!!
    }
}

int main() {
    const int total_threads = 5;
    std::vector<std::thread> threads;

    for (int i = 0; i < max_counter; ++i) {
        threads.emplace_back(std::thread(&DoWork, i, total_threads));
    }

    for (int i = 0; i < max_counter; ++i) {
        threads[i].join();
    }

    printf("Main Thread Finished!\n");
}

生产者消费者模型

#include <iostream>
#include <mutex>
#include <thread>
#include <queue>
#include <condition_variable>

template <typename T>
class TaskQueue {
 private:
    std::mutex mutex_;
    size_t capacity_;
    std::queue<T> queue_;

    std::condition_variable is_not_full_cond_;
    std::condition_variable is_not_empty_cond_;

    bool IsFull() {
        return queue_.size() == capacity_;
    }

    bool IsEmpty() {
        return queue_.size() == 0;
    }

 public:
    explicit TaskQueue(size_t capacity) : capacity_(capacity) {}

    void Push(T& elem) {
        std::unique_lock<std::mutex> locker(mutex_);
        {
            is_not_full_cond_.wait(locker, [this]() {
                return !IsFull();
            });

            queue_.emplace(elem);            
        }
        locker.unlock();
        is_not_empty_cond_.notify_one();
    }

    T Pop() {
        T ret;
        std::unique_lock<std::mutex> locker(mutex_);
        {
            is_not_empty_cond_.wait(locker, [this]() {
                return !IsEmpty();
            });
            ret = queue_.front();
            queue_.pop();
        }
        locker.unlock();
        is_not_full_cond_.notify_one();
        return ret;
    }
};

void Producer(TaskQueue<int>& q, int num) {
    while (num-- > 0) {
        int tmp = ::rand() % 100;
        std::cout << "Producer: " << tmp << std::endl;
        q.Push(tmp);
    }
}

void Consumer(TaskQueue<int>& q, int num) {
    while (num-- > 0) {
        std::cout << "Consumer: " << q.Pop() << std::endl;
    }
}

int main() {
    ::srand(::time(nullptr));

    TaskQueue<int> q(10);
    std::thread producer(&Producer, std::ref(q), 100);
    std::thread consumer(&Consumer, std::ref(q), 100);

    producer.join();
    consumer.join();
}

手写线程池

#include <unistd.h>

#include <thread>
#include <iostream>
#include <mutex>
#include <thread>
#include <queue>
#include <condition_variable>
#include <memory>

template <typename T>
class TaskQueue {
 private:
    std::mutex mutex_;
    size_t capacity_;
    std::queue<T> queue_;

    std::condition_variable is_not_full_cond_;
    std::condition_variable is_not_empty_cond_;

    bool IsFull() {
        return queue_.size() == capacity_;
    }

    bool IsEmpty() {
        return queue_.size() == 0;
    }

 public:
    explicit TaskQueue(size_t capacity) : capacity_(capacity) {}

    template<typename U>
    void Push(U&& elem) {
        std::unique_lock<std::mutex> locker(mutex_);
        {
            is_not_full_cond_.wait(locker, [this]() {
                return !IsFull();
            });

            queue_.emplace(std::forward<U>(elem));            
        }
        locker.unlock();
        is_not_empty_cond_.notify_one();
    }

    T Pop() {
        T ret;
        std::unique_lock<std::mutex> locker(mutex_);
        {
            is_not_empty_cond_.wait(locker, [this]() {
                return !IsEmpty();
            });
            ret = std::move(queue_.front());
            queue_.pop();
        }
        locker.unlock();
        is_not_full_cond_.notify_one();
        return ret;
    }
};

class Task {
 public:
    Task() {}

    virtual ~Task() {}

    virtual int Do() = 0;
};

class MyTask : public Task {
 public:
    MyTask() {}

    int Do() override {
        std::cout << "MyTask tid=" << ::gettid() << std::endl;
        return 0;
    }
};

class ExitTask : public Task {
 public:
    ExitTask() {}

    int Do() override {
        std::cout << "Thread " << ::gettid() << " exit!" << std::endl;
        return -1;
    }
};

class ThreadPool {
 private:
    std::vector<std::thread> threads_;
    int thread_num_;

    TaskQueue<std::unique_ptr<Task>> queue_;

 public:
    ThreadPool(int thread_num, int queue_size)
     : thread_num_(thread_num), queue_(queue_size) {}

    void Start() {
        for (auto i = 0; i < thread_num_; ++i) {
            threads_.emplace_back(std::thread(&ThreadPool::DoWork, this));
        }
    }

    void Exit() {
        for (auto i = 0; i < thread_num_; ++i) {
            queue_.Push(std::move(std::make_unique<ExitTask>()));
        }

        for (auto& thread : threads_) {
            thread.join();
        }
    }

    void AddTask(std::unique_ptr<Task> task) {
        queue_.Push(std::move(task));
    }

 private:
    void DoWork() {
        while (true) {
            auto task = queue_.Pop();
            auto ret = task->Do();
            if (ret) break;
        }
    }
};

int main() {
    ThreadPool thread_pool(4, 4);
    thread_pool.Start();
    for (int i = 0; i < 20; ++i) {
        thread_pool.AddTask(std::make_unique<MyTask>());
    }
    thread_pool.Exit();

    std::cout << "Main Thread Exit!" << std::endl;

    return 0;
}

手写读写锁

读者优先锁

纯二值信号量(互斥锁)版本

#include <thread>
#include <mutex>
#include <iostream>

class MySharedLock {
 private:
    std::mutex writer_lock_;
    std::mutex reader_counter_lock_;
    int reader_counter_ = 0;

 public:
    void Lock() {
        writer_lock_.lock();
    }

    void Unlock() {
        writer_lock_.unlock();
    }

    void LockShared() {
        reader_counter_lock_.lock();
        {
            ++reader_counter_;
            if (reader_counter_ == 1) {
                writer_lock_.lock();
            }
        }
        reader_counter_lock_.unlock();
    }

    void UnlockShared() {
        reader_counter_lock_.lock();
        {
            --reader_counter_;
            if (reader_counter_ == 0) {
                writer_lock_.unlock();
            }
        }
        reader_counter_lock_.unlock();
    }
};

int resource = 0;
int max_resource = 1000;

MySharedLock shared_lock;

void WriterWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.Lock();
        {
            // Write action...
            ++resource;
            printf("write resource=%d\n", resource);
            flag = resource < max_resource;
        }
        shared_lock.Unlock();
    }
}

void ReaderWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.LockShared();
        {
            // Read action...
            printf("read resource=%d\n", resource);
            flag = resource < max_resource;            
        }
        shared_lock.UnlockShared();
    }
}

int main() {
    std::thread reader1(&ReaderWorker);
    std::thread reader2(&ReaderWorker);
    std::thread writer1(&WriterWorker);
    std::thread writer2(&WriterWorker);

    reader1.join();
    reader2.join();
    writer1.join();
    writer2.join();
}

条件变量版本

#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>

class MySharedLock {
 private:
    std::mutex mutex_;
    std::condition_variable cond_read_;
    std::condition_variable cond_write_;
    int active_readers_ = 0;
    int active_writers_ = 0;
    int waiting_readers_ = 0;
    int waiting_writers_ = 0;

 public:
    void LockShared() {
        std::unique_lock<std::mutex> mtx(mutex_);

        ++waiting_readers_;

        cond_read_.wait(mtx, [this]() {
            return !(active_writers_ > 0);
        });

        --waiting_readers_;
        ++active_readers_;
    }

    void UnlockShared() {
        std::unique_lock<std::mutex> mtx(mutex_);

        if (--active_readers_ == 0 && waiting_writers_ > 0) {
            cond_write_.notify_one();
        }
    }

    void Lock() {
        std::unique_lock<std::mutex> mtx(mutex_);

        ++waiting_writers_;

        cond_write_.wait(mtx, [this]() {
            return !(active_readers_ > 0 || active_writers_ > 0);
        });

        --waiting_writers_;
        ++active_writers_;
    }

    void Unlock() {
        std::unique_lock<std::mutex> mtx(mutex_);

        --active_writers_;
        if (waiting_readers_ > 0) {
            cond_read_.notify_all();
        }
        else if (waiting_writers_ > 0) {
            cond_write_.notify_one();
        }
    }
};

int resource = 0;
int max_resource = 1000;

MySharedLock shared_lock;

void WriterWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.Lock();
        {
            // Write action...
            ++resource;
            printf("write resource=%d\n", resource);
            flag = resource < max_resource;
        }
        shared_lock.Unlock();
    }
}

void ReaderWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.LockShared();
        {
            // Read action...
            printf("read resource=%d\n", resource);
            flag = resource < max_resource;            
        }
        shared_lock.UnlockShared();
    }
}

int main() {
    std::thread reader1(&ReaderWorker);
    std::thread reader2(&ReaderWorker);
    std::thread writer1(&WriterWorker);
    std::thread writer2(&WriterWorker);

    reader1.join();
    reader2.join();
    writer1.join();
    writer2.join();
}

写者优先锁

纯二值信号量(互斥锁)版本

#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>

class MySharedLock {
 private:
    int reader_count_ = 0;
    int writer_count_ = 0;
    std::mutex reader_count_protector_;
    std::mutex writer_count_protector_;

    std::mutex resource_protector_;

    std::mutex reader_mtx_;

 public:
    void Lock() {
        writer_count_protector_.lock();
        {
            ++writer_count_;
            if (writer_count_ == 1) {
                reader_mtx_.lock();
            }
        }
        writer_count_protector_.unlock();

        resource_protector_.lock();
    }

    void Unlock() {
        resource_protector_.unlock();

        writer_count_protector_.lock();
        {
            --writer_count_;
            if (writer_count_ == 0) {
                reader_mtx_.unlock();
            }
        }
        writer_count_protector_.unlock();
    }

    void LockShared() {
        reader_mtx_.lock();
        {
            reader_count_protector_.lock();
            {
                ++reader_count_;
                if (reader_count_ == 1) {
                    resource_protector_.lock();
                }
            }
            reader_count_protector_.unlock();
        }
        reader_mtx_.unlock();
    }

    void UnlockShared() {
        reader_count_protector_.lock();
        {
            --reader_count_;
            if (reader_count_ == 0) {
                resource_protector_.unlock();
            }
        }
        reader_count_protector_.unlock();
    }
};

int resource = 0;
int max_resource = 1000;

MySharedLock shared_lock;

void WriterWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.Lock();
        {
            // Write action...
            ++resource;
            printf("write resource=%d\n", resource);
            flag = resource < max_resource;
        }
        shared_lock.Unlock();
    }
}

void ReaderWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.LockShared();
        {
            // Read action...
            printf("read resource=%d\n", resource);
            flag = resource < max_resource;            
        }
        shared_lock.UnlockShared();
    }
}

int main() {
    std::thread reader1(&ReaderWorker);
    std::thread reader2(&ReaderWorker);
    std::thread writer1(&WriterWorker);
    std::thread writer2(&WriterWorker);

    reader1.join();
    reader2.join();
    writer1.join();
    writer2.join();
}

条件变量版本

#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>

class MySharedLock {
 private:
    std::mutex mutex_;
    std::condition_variable cond_read_;
    std::condition_variable cond_write_;
    int active_readers_ = 0;
    int active_writers_ = 0;
    int waiting_readers_ = 0;
    int waiting_writers_ = 0;

 public:
    void LockShared() {
        std::unique_lock<std::mutex> mtx(mutex_);

        ++waiting_readers_;

        cond_read_.wait(mtx, [this]() {
            return !(active_writers_ > 0 || waiting_writers_ > 0);
        });

        --waiting_readers_;
        ++active_readers_;
    }

    void UnlockShared() {
        std::unique_lock<std::mutex> mtx(mutex_);

        if (--active_readers_ == 0 && waiting_writers_ > 0) {
            cond_write_.notify_one();
        }
    }

    void Lock() {
        std::unique_lock<std::mutex> mtx(mutex_);

        ++waiting_writers_;

        cond_write_.wait(mtx, [this]() {
            return !(active_readers_ > 0 || active_writers_ > 0);
        });

        --waiting_writers_;
        ++active_writers_;
    }

    void Unlock() {
        std::unique_lock<std::mutex> mtx(mutex_);

        --active_writers_;
        if (waiting_writers_ > 0) {
            cond_write_.notify_one();
        }
        else if (waiting_readers_ > 0) {
            cond_read_.notify_all();
        }
    }
};

int resource = 0;
int max_resource = 1000;

MySharedLock shared_lock;

void WriterWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.Lock();
        {
            // Write action...
            ++resource;
            printf("write resource=%d\n", resource);
            flag = resource < max_resource;
        }
        shared_lock.Unlock();
    }
}

void ReaderWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.LockShared();
        {
            // Read action...
            printf("read resource=%d\n", resource);
            flag = resource < max_resource;            
        }
        shared_lock.UnlockShared();
    }
}

int main() {
    std::thread reader1(&ReaderWorker);
    std::thread reader2(&ReaderWorker);
    std::thread writer1(&WriterWorker);
    std::thread writer2(&WriterWorker);

    reader1.join();
    reader2.join();
    writer1.join();
    writer2.join();
}

公平锁

纯二值信号量(互斥锁)版本

#include <thread>
#include <mutex>
#include <iostream>

class MySharedLock {
 private:
    std::mutex serializer_;

    std::mutex writer_lock_;
    std::mutex reader_counter_lock_;
    int reader_counter_ = 0;

 public:
    void Lock() {
        serializer_.lock();
        writer_lock_.lock();
        serializer_.unlock();
    }

    void Unlock() {
        writer_lock_.unlock();
    }

    void LockShared() {
        serializer_.lock();
        reader_counter_lock_.lock();
        {
            ++reader_counter_;
            if (reader_counter_ == 1) {
                writer_lock_.lock();
            }
        }
        reader_counter_lock_.unlock();
        serializer_.unlock();
    }

    void UnlockShared() {
        reader_counter_lock_.lock();
        {
            --reader_counter_;
            if (reader_counter_ == 0) {
                writer_lock_.unlock();
            }
        }
        reader_counter_lock_.unlock();
    }
};

int resource = 0;
int max_resource = 1000;

MySharedLock shared_lock;

void WriterWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.Lock();
        {
            // Write action...
            ++resource;
            printf("write resource=%d\n", resource);
            flag = resource < max_resource;
        }
        shared_lock.Unlock();
    }
}

void ReaderWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.LockShared();
        {
            // Read action...
            printf("read resource=%d\n", resource);
            flag = resource < max_resource;            
        }
        shared_lock.UnlockShared();
    }
}

int main() {
    std::thread reader1(&ReaderWorker);
    std::thread reader2(&ReaderWorker);
    std::thread writer1(&WriterWorker);
    std::thread writer2(&WriterWorker);

    reader1.join();
    reader2.join();
    writer1.join();
    writer2.join();
}

条件变量版本

#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>

class MySharedLock {
 private:
    std::mutex mutex_;
    std::condition_variable cond_;

    int active_readers_ = 0;

    int next_ticket_ = 0;
    int latest_ticket_ = 0;

 public:
    void LockShared() {
        std::unique_lock<std::mutex> locker(mutex_);
        int ticket = latest_ticket_++;

        cond_.wait(locker, [this, ticket]() {
            return ticket == next_ticket_;
        });

        ++next_ticket_;
        ++active_readers_;
        cond_.notify_all();
    }

    void UnlockShared() {
        std::unique_lock<std::mutex> locker(mutex_);
        --active_readers_;
        cond_.notify_all();
    }

    void Lock() {
        std::unique_lock<std::mutex> locker(mutex_);
        int ticket = latest_ticket_++;

        cond_.wait(locker, [this, ticket]() {
            return ticket == next_ticket_ && active_readers_ <= 0;
        });
    }

    void Unlock() {
        std::unique_lock<std::mutex> locker(mutex_);
        ++next_ticket_;
        cond_.notify_all();
    }
};

int resource = 0;
int max_resource = 1000;

MySharedLock shared_lock;

void WriterWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.Lock();
        {
            // Write action...
            ++resource;
            printf("write resource=%d\n", resource);
            flag = resource < max_resource;
        }
        shared_lock.Unlock();
    }
}

void ReaderWorker() {
    bool flag = true;
    while (flag) {
        shared_lock.LockShared();
        {
            // Read action...
            printf("read resource=%d\n", resource);
            flag = resource < max_resource;            
        }
        shared_lock.UnlockShared();
    }
}

int main() {
    std::thread reader1(&ReaderWorker);
    std::thread reader2(&ReaderWorker);
    std::thread writer1(&WriterWorker);
    std::thread writer2(&WriterWorker);

    reader1.join();
    reader2.join();
    writer1.join();
    writer2.join();
}