21xrx.com
2025-03-31 06:03:58 Monday
文章检索 我的文章 写文章
《C++多线程开发--入门到精通》
2023-07-07 18:57:32 深夜i     6     0
C++ 多线程 开发 入门 精通
return stop || !tasks.empty();

C++是一种非常强大的编程语言,在多线程开发方面更是拥有无限潜力。C++多线程开发可以帮助开发人员更灵活地控制程序的并发性和性能,使得程序可以更加高效地运行。如果您想要学习C++多线程开发,那么这篇文章将会给您提供一些重要的信息,从入门到进阶,帮助您成为一名精通C++多线程开发的专家。

入门

对于初学者来说,了解C++多线程开发的基础概念是非常重要的。多线程开发可以让CPU同时处理多个任务,提高了程序的效率。在C++中,使用Thread类可以创建一个新线程,从而实现并发操作。例如:

#include <iostream>
#include <thread>
using namespace std;
void myFunction()
  cout << "Hello World!" << endl;
int main() {
  thread t(myFunction);
  t.join();
  return 0;
}

在这个例子中,我们定义了一个函数myFunction(),并使用Thread类创建了一个新线程t。在主线程中,使用join()函数等待新线程的执行结束。当程序运行时,可以看到"Hello World!"的输出。

进阶

一旦您开始感受到多线程的威力,您将希望学习更高级的概念,例如线程同步、互斥锁和条件变量等。这些概念是C++多线程开发中至关重要的,因为它们帮助避免不同线程之间的访问冲突,确保程序的正常运行。

线程同步是指为了避免两个或多个线程在同一个时间访问相同的内存空间,而需要协调和同步访问的机制。例如,在银行管理系统中,如果两个或多个线程同时操作同一个银行账户,就会有竞态条件的问题。如果不处理这个问题,就可能导致账户余额出现错误。因此,我们需要使用互斥锁和条件变量等机制来保证线程同步和安全。

互斥锁是一种保护共享资源的机制。在使用互斥锁时,如果一个线程正在访问共享资源,其他线程将被阻塞,直到第一个线程释放锁为止。例如:

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int value = 0;
mutex mtx;
void myFunction() {
  for (int i = 0; i < 100000; i++) {
    mtx.lock();
    value++;
    mtx.unlock();
  }
}
int main() {
  thread t1(myFunction);
  thread t2(myFunction);
  t1.join();
  t2.join();
  cout << "Value: " << value << endl;
  return 0;
}

在这个例子中,我们定义了一个共享变量value,并使用互斥锁mtx来保护它。在myFunction()函数中,我们使用mtx.lock()来获取锁,并使用mtx.unlock()释放锁,从而保证了共享变量value的访问安全和同步。

条件变量则是典型的线程同步机制,可以用于等待某个事件发生,从而使线程之间相互协作。例如:

#include <iostream>
#include <thread>
#include <condition_variable>
#include <queue>
using namespace std;
queue<int> myQueue;
mutex mtx;
condition_variable notEmpty;
void myFunction() {
  for (int i = 0; i < 5; i++) {
    unique_lock<mutex> lock(mtx);
    while (myQueue.empty()) {
      notEmpty.wait(lock);
    }
    int value = myQueue.front();
    myQueue.pop();
    cout << "Value: " << value << endl;
  }
}
int main() {
  thread t(myFunction);
  for (int i = 0; i < 5; i++) {
    unique_lock<mutex> lock(mtx);
    myQueue.push(i);
    notEmpty.notify_one();
  }
  t.join();
  return 0;
}

在这个例子中,我们使用条件变量notEmpty通知另一个线程myFunction()去读取myQueue队列中的值。在myFunction()函数中,我们使用unique_lock 来获取锁,并使用while (myQueue.empty())和notEmpty.wait(lock)等待条件变量notEmpty的通知,从而避免了CPU的空转消耗。

精通

当您精通了C++多线程开发的基础概念和使用方法后,您可以开始研究更高级的概念和模式,例如可重入代码和线程池等。

可重入代码是指一个函数或程序可以被多个线程同时执行而不会出现问题。可重入代码需要满足两个条件:首先是不使用全局变量或静态变量;其次是使用互斥锁或条件变量等机制来保证线程同步。例如:

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
class MyClass {
public:
  MyClass() : value(0) {}
  // 可重入函数
  int getValue() {
    unique_lock<mutex> lock(mtx);
    int temp = value;
    return temp;
  }
  // 可重入函数
  void setValue(int val) {
    unique_lock<mutex> lock(mtx);
    value = val;
  }
private:
  int value;
  mutex mtx;
};
int main() {
  MyClass myClass;
  thread t1([&myClass]{
    for (int i = 0; i < 100000; i++) {
      myClass.setValue(myClass.getValue() + 1);
    }
  });
  thread t2([&myClass]{
    for (int i = 0; i < 100000; i++) {
      myClass.setValue(myClass.getValue() + 1);
    }
  });
  t1.join();
  t2.join();
  cout << "Value: " << myClass.getValue() << endl;
  return 0;
}

在这个例子中,我们定义了一个可重入类MyClass,并使用互斥锁mtx保证了它的线程安全性。在主函数中,我们创建了两个线程t1和t2,它们同时访问了MyClass类的setValue()和getValue()函数。但是,由于这两个函数都是可重入的,因此不会出现竞态条件问题。

另一个重要的概念是线程池。线程池是一种管理线程的机制,可以将一组线程放入一个缓存区域中,等待执行。这种方法可以避免为每个任务重新创建线程的开销,提高了程序的效率。例如:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
using namespace std;
class ThreadPool {
public:
  ThreadPool() {
    stop = false;
    threadCount = thread::hardware_concurrency();
    for (int i = 0; i < threadCount; i++) {
      threads.emplace_back([this]{
        while (true) {
          function<void()> task;
          {
            unique_lock<mutex> lock(mtx);
            condition.wait(lock, [this]{ return stop || !tasks.empty(); });
            if (stop && tasks.empty())
              return;
            
            task = move(tasks.front());
            tasks.pop();
          }
          task();
        }
      });
    }
  }
  ~ThreadPool() {
    {
      unique_lock<mutex> lock(mtx);
      stop = true;
    }
    condition.notify_all();
    for (auto& thread : threads) {
      thread.join();
    }
  }
  template<typename F, typename... Args>
  void enqueue(F&& f, Args&&... args) {
    {
      unique_lock<mutex> lock(mtx);
      tasks.emplace([=]{
        f(args...);
      });
    }
    condition.notify_one();
  }
private:
  vector<thread> threads;
  queue<function<void()>> tasks;
  mutex mtx;
  condition_variable condition;
  bool stop;
  int threadCount;
};
void myFunction(int i)
  cout << "Hello " << i << "!" << endl;
int main() {
  ThreadPool pool;
  for (int i = 0; i < 10; i++) {
    pool.enqueue(myFunction, i);
  }
  return 0;
}

在这个例子中,我们定义了一个ThreadPool类,并实现了enqueue()方法来将任务加入到任务队列中。在ThreadPool类的构造函数中,我们创建了threadCount个线程,并不断地从任务队列中取出任务来执行。在主函数中,我们向线程池中添加了10个任务,并等待它们完成。

总结

C++多线程开发是一个非常有用的工具,可以提高程序的效率和运行速度。通过本文介绍的基础、进阶和高级概念,您可以更好地掌握C++多线程开发的方法和技巧。当您精通了这些内容后,您就可以以更高效的方式编写程序,为项目的成功贡献力量。

  
  

评论区