[✔️]Thread多线程编程

简介: [✔️]Thread多线程编程

系统线程函数


pthread_create


创建线程,确定线程的入口函数,返回值0表示成功,非0表示出错


  • Linux、Mac


#include <pthread.h>
int pthread_create(
  pthread_t *restrict tidp,             //新创建的线程ID指向的内存单元。
  const pthread_attr_t *restrict attr,  //线程属性,默认为NULL
  void *(*start_rtn)(void *),           //新创建的线程从start_rtn函数的地址开始运行
  void *restrict arg                    //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。
);


  • Windows


HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes, // 定义新线程的安全属性,一般为NULL
    DWORD dwStackSize,// 分配线程堆栈的大小,默认值为0
    LPTHREAD_START_ROUTINE lpStartAddress,// 线程函数地址
    LPVOID lpParameter,// 传递给线程函数的参数
    DWORD dwCreationFlags,// 创建线程的运行状态,CREATE_SUSPEND表示挂起当前创建的线程,0表示立即执行当前创建的进程
    LPDWORD lpThreadID// 返回新创建线程的ID
);


pthread_mutex_init


初始化互斥锁,互斥锁可以是线程按顺序执行,


  • Linux、Mac


int pthread_mutex_init(
    pthread_mutex_t *           restrict mutex,
    const pthread_mutexattr_t * restrict attr // 互斥锁属性,如果为null,则使用默认的快速互斥锁
);


  • Windows


找出当前系统中是否存在指定进程的实例,如果没有,则创建一个互斥体


执行成功就返回互斥体的句柄


HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
 BOOL bInitialOwner,                      // 初始化互斥对象的所有者
 LPCTSTR lpName                           // 指向互斥对象名的指针
);


STL Thread


std::thread


#include <stdio.h>
#include <iostream>
#include <thread>
using namespace std;
int n = 0;
void count(int num){
    for (int i = 0; i < num; i++)
    {
        n++;
    }
}
int main(int, char**){
    thread t1(count, 100000);
    thread t2(count, 100000);
    t2.join(); // 等待线程结束,会阻塞
    t1.join();
    cout << n << endl;
    system("pause");
    return 0;
}


没有执行joindetach的线程在程序结束时会引发异常


运行结果:117095,明显小于200000,要解决这个问题,就需要用到互斥锁


std::mutex


互斥量(mutex)从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥锁加锁的线程将会阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为运行状态的线程可以对互斥锁加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。


#include <stdio.h>
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int n = 0;
std::mutex mtx;
void count(int num){
    for (int i = 0; i < num; i++)
    {
        mtx.lock();
        n++;
        mtx.unlock();
    }
}
int main(int, char**){
    thread t1(count, 100000);
    thread t2(count, 100000);
    t2.join();
    t1.join();
    cout << n << endl;
    system("pause");
    return 0;
}


std::atomic


因为频繁的lock、unlock会有一定的性能影响,atomic就是在解决这个问题,只需要变量类型为automic_int即可,其他类型同理:


atomic_int n;


std::lock_guard


guard(警卫,看守)


锁管理器在构造函数中自动绑定它的互斥体并加锁,在析构函数中解锁,大大减少了死锁

的风险


template <class _Mutex>
class _NODISCARD lock_guard { // class with destructor that unlocks a mutex
public:
    using mutex_type = _Mutex;
    explicit lock_guard(_Mutex& _Mtx) : _MyMutex(_Mtx) { // construct and lock
        _MyMutex.lock();// 构造加锁
    }
    lock_guard(_Mutex& _Mtx, adopt_lock_t) : _MyMutex(_Mtx) {} // construct but don't lock
    ~lock_guard() noexcept {
        _MyMutex.unlock(); // 析构解锁
    }
    lock_guard(const lock_guard&) = delete;
    lock_guard& operator=(const lock_guard&) = delete;
private:
    _Mutex& _MyMutex;
};


std::unique_lock


unique_lock 是为了避免 mutex 忘记释放锁。 在对象创建时自动加锁,对象释放时自动解锁。


std::unique_lock<mutex> lock(m_Mutex);


www.jianshu.com/p/34d219380…


std::condition_variable


// thread1
while(true)
{
    pthread_mutex_lock(&mutex);
    iCount++;
    pthread_mutex_unlock(&mutex);
}
//thread 2:
while(true)
{
    pthread_mutex_lock(&mutex);
    if(iCount >= 100)
    {
        iCount = 0;
    }
    pthread_mutex_unlock(&mutex);
}


这种实现下,就算 lock 空闲,thread2需要不断重复<加锁,判断,解锁>这个流程,会给系统带来不必要的开销。有没有一种办法让 thread2先被 block,等条件满足的时候再唤醒 thread2?这样 thread2 就不用不断进行重复的加解锁操作了?这就要用到条件变量了


执行notify_one或者notify_all去唤醒阻塞线程。

目录
相关文章
|
Java 开发者
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
190 0
|
8月前
|
Arthas 监控 Java
Arthas thread(查看当前JVM的线程堆栈信息)
Arthas thread(查看当前JVM的线程堆栈信息)
1507 10
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
195 7
|
Java C# Python
线程等待(Thread Sleep)
线程等待是多线程编程中的一种同步机制,通过暂停当前线程的执行,让出CPU时间给其他线程。常用于需要程序暂停或等待其他线程完成操作的场景。不同语言中实现方式各异,如Java的`Thread.sleep(1000)`、C#的`Thread.Sleep(1000)`和Python的`time.sleep(1)`。使用时需注意避免死锁,并考虑其对程序响应性的影响。
346 8
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
272 3
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
274 2
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
227 2
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
345 1

热门文章

最新文章