Linux下的多线程编程:原理、工具及应用(1)

这篇具有很好参考价值的文章主要介绍了Linux下的多线程编程:原理、工具及应用(1)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

                                               🎬慕斯主页修仙—别有洞天

                                              ♈️今日夜电波:Flower of Life—陽花

                                                                0:34━━━━━━️💟──────── 4:46
                                                                    🔄   ◀️   ⏸   ▶️    ☰  

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

模拟语言封装Linux下多线程接口

线程互斥

前置知识

解释为什么会产生上述代码错误

如何解决?加锁!

什么是互斥锁?

pthread_mutex_t

pthread_mutex_init

PTHREAD_MUTEX_INITIALIZER

pthread_mutex_lock

pthread_mutex_unlock

pthread_mutex_trylock(不常用)

根据如上的互斥锁来进行操作

通过定义全局的锁

通过定义局部的锁(优雅的解决)


模拟语言封装Linux下多线程接口

        如下是我们以使用C++实现简单封装:

#pragma once
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <string>
#include <functional>

template <class T>
using func_t = std::function<void(T)>;

template <class T>
class Thread
{
public:
    Thread(const std::string threadname,func_t<T> func,T data)
        :_tid(0),_threadname(threadname),_isrunning(false),_func(func),_data(data)
        {}
    
    static void* ThreadRoutine(void* args)
    {
        Thread *ts=static_cast<Thread *>(args);

        ts->_func(ts->_data);

        return nullptr;
    }

    bool Start()
    {
        int n=pthread_create(&_tid,nullptr,ThreadRoutine,this);
        if(n==0)
        {
            _isrunning=true;
            return true;
        }
        return false;
    }

    bool Join()
    {
        if(!_isrunning) return true;
        int n=pthread_join(_tid,nullptr);
        if(n==0)
        {
            _isrunning=false;
            return true;
        }
        return false;

    }

    std::string ThreadName()
    {
        return _threadname;
    }

    bool Isrunning()
    {
        return _isrunning;
    }

private:
    pthread_t _tid;
    std::string _threadname;
    bool _isrunning;
    func_t<T> _func;
    T _data;
};

        其中成员变量存储了进程线程的ID、线程名、线程运行状态、线程运行的函数以及线程传递的变量。实现了线程的构造函数(根据线程名、传入函数以及传入变量构造),实现了开始运行的操作、等待线程的操作、判断是否运行以及返回线程名的操作

        重点说一下ThreadRoutine这个函数!他实现的是配合运行操作为pthread_create传递函数在执行完后return nullptr结束线程的操作!那他为啥要定义成如下的形式呢?

    static void* ThreadRoutine(void* args)
    {
        Thread *ts=static_cast<Thread *>(args);

        ts->_func(ts->_data);

        return nullptr;
    }

        这是因为成员函数是默认会带有this指针的,而我们要传入pthread_create中规定了只能传入void* args变量的函数。因此我们使用static让他不具有this指针!当然,这并不是唯一的解决办法,我们也可以将他定义到类外,然后给在类内声明友元函数即可

线程互斥

        我们根据上面所封装的线程库来叙写了如下的代码:

#include "Thread.hpp"

std::string GetThreadName()
{
    static int number = 1;
    char name[64];
    snprintf(name, sizeof(name), "Thread-%d", number++);
    return name;
}

void Print(int num)
{
    while (num)
    {
        std::cout << "hello world: " << num-- << std::endl;
        sleep(1);
    }
}



int ticket = 10000; // 全局的共享资源


void GetTicket(std::string name)
{
    while (true)
    {
       
        if (ticket > 0) 
        {
            // 充当抢票花费的时间
            usleep(1000);
            printf("%s get a ticket: %d\n", name.c_str(), ticket);
            ticket--;
        }
        else
        {

            break;
        }
        // 实际情况,还有后续的动作, TODO?
    }
}


int main()
{
    std::string name1 = GetThreadName();
    Thread<std::string> t1(name1, GetTicket, name1);

    std::string name2 = GetThreadName();
    Thread<std::string> t2(name2, GetTicket, name2);

    std::string name3 = GetThreadName();
    Thread<std::string> t3(name3, GetTicket, name3);

    std::string name4 = GetThreadName();
    Thread<std::string> t4(name4, GetTicket, name4);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();

    return 0;
}

        该代码简单的模拟了一个抢票的场景,其中票数仅为tickets=10000,在tickets减为0的时候就会停止抢票。我们创建4个线程来模拟多人抢票的情况。按照代码的原意,四个线程会依次抢票知道票数为0为止,但是代码真的如我们所想的那样嘛?

Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

        从上面的现象我们很容易的发现我们的共享资源tickets发生了不该发生的操作,出现抢到同一张票以及多抢了的情况,出现了数据不一致的情况!这是为什么呢?下面先了解一些前置知识:

前置知识

        我们将任何一个时刻,只允许一个进程正在访问的资源称为临界资源。把进程中访问临界资源的代码叫做临界区。例如我们上述的代码:

Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

        互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用 。图解如下:

Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

        原子性(后面讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成。

 

解释为什么会产生上述代码错误

        请先看以下代码:

#include<iostream>

int main()
{
	int a = 10;
	a++;
	return 0;
}

        很平常的一段代码,但是这也是我们常出现错误代码中访问临界资源的代码。我们可以从反汇编中看出问题:

Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

        可以看到通过反汇编,我们发现本来是一段代码的操作,在底层居然被翻译成了三条操作。这也就违背了我们的原子性!这三条语句在多线程并发访问的时候都有可能会被中断!大致图解:

Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

        当我们知道实际上访问临界资源时是有三步操作后,我们就可以理解为什么会产生如上的错误了!看完如下的例子就明白了:假设现在我们启动了两个线程,他们都要执行如上的三步。线程1先是访问该临界资源,但是在他执行完几次完整的访问(3步都走完)后,它的时间片用完了,恰好此时该线程卡在第二步的--操作,此时他要保存上下文,eax中存储的值为7,然后轮到下一个进程执行。下一个进程也是执行完整了几步,恰好也是在第二步的时候他的时间片用完了,此时他也要保存上下文,eax中存储的值为3。接着轮到第一个线程,他需要恢复上下文啊,因此,从第三步开始将eax返回内存中,此时!count又变回了7!!!

        看完上面的例子你大概就明白了,为什么上述代码互产生错误的原因。因为多线程并发访问全部int,不是原子的!!!会有数据不一致的并发访问问题!

        看完上述的解释是不是以为完了?当然没有!我们都知道在CPU中我们存在着:算术运算、逻辑运算、处理内外中断、控制单元的操作。在上述出错代码中我们还存在着if的判断语句,这就是一种逻辑运算,底层是需要两步的处理:1、加载如寄存器。2、判断他也不是原子的!也会出现会有数据不一致的并发访问问题!因此,我们根本就不知道每个进程的具体执行状况!这也是为什么该数据会减到负数的原因,因为有可能if判断都认为是符合条件的!但是实际寄存器中的值确是不符合的!

如何解决?加锁!

什么是互斥锁?

        互斥锁是一种同步机制,用于确保在多线程环境中共享资源的安全访问

        互斥锁的核心作用是防止多个线程同时访问和修改共享资源,从而避免数据竞争和不一致的问题。以下是互斥锁的一些关键特性和概念:

  1. 排他性:互斥锁确保在任何时刻,只有一个线程能够持有锁并访问共享资源。当一个线程获得锁时,其他线程必须等待直到锁被释放。
  2. 同步原语:互斥锁是一种同步原语,它通常与条件变量、信号量等其他同步机制一起使用,以实现复杂的线程间协作和通信。
  3. 初始化:在使用互斥锁之前,需要对其进行初始化,这可以通过pthread_mutex_init函数完成,也可以静态地通过PTHREAD_MUTEX_INITIALIZER宏来初始化一个互斥锁变量。
  4. 加锁与解锁:线程在访问共享资源前需要对互斥锁进行加锁(上锁),访问完成后需要释放互斥锁(解锁)。这一过程通常通过pthread_mutex_lockpthread_mutex_unlock函数来实现。
  5. 性能考量:互斥锁可能导致进程睡眠和唤醒,以及上下文切换,这些都会带来一定的性能开销。因此,互斥锁适用于加锁时间较长的场景,以减少频繁的锁争用和上下文切换。
  6. 销毁:当互斥锁不再使用时,应当通过pthread_mutex_destroy函数进行销毁,以避免资源泄漏。

pthread_mutex_t

    pthread_mutex_t是一个数据类型,用于表示互斥锁(Mutex)对象。在多线程编程中,互斥锁是一种同步机制,用于保护共享资源,防止多个线程同时访问和修改这些资源,从而避免数据竞争和不一致的问题。

   pthread_mutex_t类型的变量通常用于声明一个互斥锁对象,并使用pthread_mutex_init函数进行初始化。初始化后,可以使用pthread_mutex_lock函数对互斥锁进行加锁操作,使用pthread_mutex_unlock函数进行解锁操作。

        需要注意的是,在使用完互斥锁后,应该及时销毁它,以避免资源泄漏。

pthread_mutex_init

    pthread_mutex_init是一个用于初始化互斥锁的函数,它是POSIX线程库(Pthreads)中的一部分。互斥锁是一种同步原语,用于保护共享资源,防止多个线程同时访问。

        函数原型:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

        参数说明:

  • mutex:指向要初始化的互斥锁对象的指针。
  • attr:指向互斥锁属性对象的指针,可以设置为NULL,表示使用默认属性。

        返回值:

  • 成功时,返回0;
  • 失败时,返回一个非零错误码。

PTHREAD_MUTEX_INITIALIZER

    PTHREAD_MUTEX_INITIALIZER是一个宏定义,用于初始化一个互斥锁对象。它通常与静态分配的互斥锁变量一起使用,以确保在多线程环境中对共享资源的访问是安全的。

        该宏定义的作用是将互斥锁对象的值设置为默认状态,以便在程序启动时立即可用。具体来说,它将互斥锁对象的类型设置为pthread_mutex_t,并将其属性设置为默认值(通常是快速互斥锁)。

pthread_mutex_lock

    pthread_mutex_lock是一个函数,用于对互斥锁进行加锁操作。它的作用是确保在多线程环境中,只有一个线程可以访问共享资源,从而避免数据竞争和不一致的问题。

        函数原型:

int pthread_mutex_lock(pthread_mutex_t *mutex);

        其中,mutex参数是一个指向互斥锁对象的指针。

        当一个线程调用pthread_mutex_lock函数时,它会尝试获取互斥锁。如果互斥锁当前未被其他线程持有,则该线程成功获取互斥锁并继续执行后续代码。如果互斥锁已经被其他线程持有,则该线程会被阻塞,直到互斥锁被释放为止。

        一旦线程成功获取互斥锁,其他试图获取该互斥锁的线程将会被阻塞,直到当前持有互斥锁的线程调用pthread_mutex_unlock函数释放互斥锁。

        需要注意的是,在使用完互斥锁后,应该及时调用pthread_mutex_unlock函数来释放互斥锁,以避免死锁或资源泄漏的情况发生。

pthread_mutex_unlock

    pthread_mutex_unlock是一个函数,用于对互斥锁进行解锁操作。它的作用是释放当前线程持有的互斥锁,以便其他线程可以获取该互斥锁并访问共享资源。

        函数原型:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

        其中,mutex参数是一个指向互斥锁对象的指针。

        当一个线程调用pthread_mutex_unlock函数时,它会尝试释放当前线程持有的互斥锁。如果当前线程确实持有该互斥锁,则该函数会成功释放互斥锁并返回0;否则,该函数会返回错误码。

        需要注意的是,在使用完互斥锁后,应该及时调用pthread_mutex_unlock函数来释放互斥锁,以避免死锁或资源泄漏的情况发生。

pthread_mutex_trylock(不常用)

    pthread_mutex_trylock是一个函数,用于尝试对互斥锁进行加锁操作。它的作用是尝试获取互斥锁,如果互斥锁当前未被其他线程持有,则该线程成功获取互斥锁并继续执行后续代码;如果互斥锁已经被其他线程持有,则该线程不会阻塞,而是立即返回错误码。

        函数原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex);

        其中,mutex参数是一个指向互斥锁对象的指针。

        当一个线程调用pthread_mutex_trylock函数时,它会尝试获取互斥锁。如果互斥锁当前未被其他线程持有,则该线程成功获取互斥锁并继续执行后续代码;如果互斥锁已经被其他线程持有,则该线程不会阻塞,而是立即返回错误码。

        需要注意的是,在使用完互斥锁后,应该及时调用pthread_mutex_unlock函数来释放互斥锁,以避免死锁或资源泄漏的情况发生。

根据如上的互斥锁来进行操作

通过定义全局的锁

        如下我们根据互斥锁来解决上述的问题:

#include <iostream>
#include <unistd.h>
#include <vector>
#include <cstdio>
#include "Thread.hpp"

// 应用方的视角
std::string GetThreadName()
{
    static int number = 1;
    char name[64];
    snprintf(name, sizeof(name), "Thread-%d", number++);
    return name;
}

void Print(int num)
{
    while (num)
    {
        std::cout << "hello world: " << num-- << std::endl;
        sleep(1);
    }
}

int ticket = 10000; // 全局的共享资源
// 共享资源了
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 锁就有了,被定义并初始化了,这个锁也是全局的哦!!

// 加锁:
// 1. 我们要尽可能的给少的代码块加锁
// 2. 一般加锁,都是给临界区加锁
void GetTicket(std::string name)
{
    while (true)
    {
        // 2. 是由程序员自己保证的!规则都必须先申请锁
        // 3. 根据互斥的定义,任何时刻,只允许一个线程申请锁成功!多个线程申请锁失败,失败的线程怎么办?在mutex上进行阻塞,本质就是等待!
        pthread_mutex_lock(&mutex); // 1. 申请锁本身是安全的,原子的,为什么?
        if (ticket > 0) // 4. 一个线程在临界区中访问临界资源的时候,可不可能发生切换?可能,完全允许!!
        {
            // 充当抢票花费的时间
            usleep(1000);
            printf("%s get a ticket: %d\n", name.c_str(), ticket);
            ticket--;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
        // 实际情况,还有后续的动作, TODO?
    }
}


int main()
{
    std::string name1 = GetThreadName();
    Thread<std::string> t1(name1, GetTicket, name1);

    std::string name2 = GetThreadName();
    Thread<std::string> t2(name2, GetTicket, name2);

    std::string name3 = GetThreadName();
    Thread<std::string> t3(name3, GetTicket, name3);

    std::string name4 = GetThreadName();
    Thread<std::string> t4(name4, GetTicket, name4);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();

    return 0;
}

Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

        可以看到我们成功的解决了上述的问题,但是加了互斥锁也让我们的执行速度相对于上面的执行速度变慢了许多。这是因为:当一个线程持有互斥锁时,其他试图访问相同共享资源的线程必须等待,直到锁被释放。这种等待会导致线程阻塞,减少了并行执行的机会。当线程在等待锁的过程中,操作系统可能会将其置于睡眠状态,并在锁可用时再次唤醒它。这种从睡眠到唤醒的过程涉及到上下文切换,这是一种相对耗时的操作。频繁的上下文切换会显著增加程序的执行时间。

通过定义局部的锁(优雅的解决)

        如下我们定义一个LockGuard.hpp的文件,该文件封装了一个可以通过构造以及析构完成对应的加锁以及解锁的操作:

#pragma once

#include <pthread.h>

// 不定义锁,默认认为外部会给我们传入锁对象
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock):_lock(lock)
    {}
    void Lock()
    {
        pthread_mutex_lock(_lock);
    }
    void Unlock()
    {
        pthread_mutex_unlock(_lock);
    }
    ~Mutex()
    {}

private:
    pthread_mutex_t *_lock;
};

class LockGuard
{
public:
    LockGuard(pthread_mutex_t *lock): _mutex(lock)
    {
        _mutex.Lock();
    }
    ~LockGuard()
    {
        _mutex.Unlock();
    }
private:
    Mutex _mutex;
};

        主函数:

        可以看到我们在临界区中定义了上述LockGuard.hpp的lockguard变量,我们可以通过上述的构造以及析构进行加锁、解锁需要注意的是:临界区代码中可以发现我们使用了一个{}来括起来,这是表示代码块的意思,可以理解变量同在函数栈帧中一样。我们通过新定义的ThreadData类来传递给之前封装的多线程接口,优雅的实现了如下的代码:

#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <cstdio>
#include "Thread.hpp"
#include "LockGuard.hpp"

// 应用方的视角
std::string GetThreadName()
{
    static int number = 1;
    char name[64];
    snprintf(name, sizeof(name), "Thread-%d", number++);
    return name;
}

void Print(int num)
{
    while (num)
    {
        std::cout << "hello world: " << num-- << std::endl;
        sleep(1);
    }
}

class ThreadData
{
public:
    ThreadData(const std::string &name, pthread_mutex_t *lock)
    : threadname(name), pmutex(lock)
    {}
public:
    std::string threadname;
    pthread_mutex_t *pmutex;
};

int ticket = 10000; // 全局的共享资源

void GetTicket(ThreadData *td)
{
    while (true)
    {
        // 非临界区代码!
        // 2. 是由程序员自己保证的!规则都必须先申请锁
        // 3. 根据互斥的定义,任何时刻,只允许一个线程申请锁成功!多个线程申请锁失败,失败的线程怎么办?在mutex上进行阻塞,本质就是等待!
       
        {
             LockGuard lockguard(td->pmutex);
            if (ticket > 0) // 4. 一个线程在临界区中访问临界资源的时候,可不可能发生切换?可能,完全允许!!
            {
                // 充当抢票花费的时间
                usleep(1000);
                printf("%s get a ticket: %d\n", td->threadname.c_str(), ticket);
                ticket--;
           
            }
            else
            {
               
                break;
            }
        }
        // 非临界区代码!
        // 实际情况,还有后续的动作, TODO?
    }
}

int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr);

    std::string name1 = GetThreadName();
    ThreadData *td = new ThreadData(name1, &mutex);
    Thread<ThreadData *> t1(name1, GetTicket, td);

    std::string name2 = GetThreadName();
    ThreadData *td2 = new ThreadData(name2, &mutex);
    Thread<ThreadData *> t2(name2, GetTicket, td2);

    std::string name3 = GetThreadName();
    ThreadData *td3 = new ThreadData(name3, &mutex);
    Thread<ThreadData *> t3(name3, GetTicket, td3);

    std::string name4 = GetThreadName();
    ThreadData *td4 = new ThreadData(name4, &mutex);
    Thread<ThreadData *> t4(name4, GetTicket, td4);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();

    pthread_mutex_destroy(&mutex);
    delete td;
    delete td2;
    delete td3;
    delete td4;

    return 0;
}

        一些知识点汇总:

// 加锁:
// 1. 我们要尽可能的给少的代码块加锁
// 2. 一般加锁,都是给临界区加锁
// 3. 个别系统,抢票代码会出现很多的票被同一个线程抢完了
// 4. 多线程运行,同一份资源,有线程长时间无法拥有,饥饿问题
// 5. 要解决饥饿问题,要让线程执行的时候,具备一定的顺序性 --- 同步

Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

 


                         感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       Linux下的多线程编程:原理、工具及应用(1),Linux练功 初阶功法,linux,运维,服务器

                                                                        给个三连再走嘛~  文章来源地址https://www.toymoban.com/news/detail-840838.html

到了这里,关于Linux下的多线程编程:原理、工具及应用(1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt的多线程编程

    并发 当有多个线程在操作时,如果系统 只有一个CPU ,则它根本不可能真正同时进行一个以上的线程,它只能把 CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其他线程处于挂起状态。 虽然看起来所有 线程都是一起执行

    2024年02月08日
    浏览(45)
  • “深入理解Java的多线程编程“

    多线程编程是指在一个程序中同时运行多个线程,以提高程序的并发性和性能。Java是一门支持多线程编程的强大编程语言,提供了丰富的多线程相关类和接口。 在Java中,可以通过以下方式实现多线程编程: 继承Thread类:创建一个继承自Thread类的子类,并重写run()方法,在

    2024年02月13日
    浏览(67)
  • C# 中的多线程和异步编程

    最近在看代码的过程中,发现有很多地方涉及到多线程、异步编程,这是比较重要且常用的知识点,而本人在这方面还理解尚浅,因此开始全面学习C#中的多线程和异步编程,文中部分内容摘抄自一位前辈的网站:网址链接,为了更便于理解和学习,本人还在个别地方做了一

    2023年04月08日
    浏览(50)
  • Android中的多线程编程与异步处理

    在移动应用开发中,用户体验是至关重要的。一个流畅、高效的应用能够吸引用户并提升用户满意度。然而,移动应用面临着处理复杂业务逻辑、响应用户输入、处理网络请求等多个任务的挑战。为了确保应用的性能和用户体验,多线程编程和异步处理成为了不可或缺的技术

    2024年02月11日
    浏览(54)
  • Python小姿势 - Python的多线程编程

    Python的多线程编程 Python的多线程编程提供了一个非常简单的方法来让一个Python程序同时运行多个任务。这个方法通过创建新的线程来实现,线程可以被视为一个单独的执行流程。 为了创建一个新线程,我们需要使用Python的_thread模块中的start_new_thread()函数。它需要两个参数:

    2024年02月04日
    浏览(39)
  • 一文读懂flutter线程: 深入了解Flutter中的多线程编程

    在移动应用开发领域,Flutter已经成为了一个备受欢迎的框架,用于创建高性能、跨平台的应用程序。Flutter的一个关键特性是其能够轻松处理多线程编程,以改进应用程序的性能和响应性。本文将深入探讨Flutter中的多线程编程,包括为什么需要多线程、如何在Flutter中创建和管

    2024年01月20日
    浏览(83)
  • 如何在Python编程中应用Linux环境下的框架,以实现高效算法?

    python是一种广泛使用的编程语言,能够帮助开发人员快速开发高效的算法。与此同时,linux环境下提供了许多优秀的框架,可以进一步提高Python编程的效率。本文将介绍如何在Python编程中应用Linux环境下的框架,以实现高效算法。 一、Python和Linux环境的优势 Python是一种易学易

    2024年02月05日
    浏览(47)
  • 深入浅出Java的多线程编程——第二篇

    目录 前情回顾 1. 中断一个线程 1.1 中断的API 1.2 小结 2. 等待一个线程  2.1 等待的API 3. 线程的状态 3.1 贯彻线程的所有状态 3.2 线程状态和状态转移的意义 4. 多线程带来的的风险-线程安全 (重点) 4.1 观察线程不安全 4.2 线程安全的概念 4.3 线程不安全的原因 4.3.1 修改共享数据

    2024年02月07日
    浏览(82)
  • 编程小白的自学笔记八(python中的多线程)

     编程小白的自学笔记七(python中类的继承) 编程小白的自学笔记六(python中类的静态方法和动态方法)  编程小白的自学笔记五(Python类的方法)  编程小白的自学笔记四(正则表达式模块search函数)  编程小白的自学笔记三(Python正则表达式)  目录 系列文章目录 前言

    2024年02月16日
    浏览(50)
  • 目标检测YOLO实战应用案例100讲-面向恶劣环境下的多模态 行人识别

    目录 前言 国内外研究现状  可见光行人目标识别  红外行人目标识别 

    2024年02月07日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包