多线程和并发之线程

news2025/6/4 7:02:26

线程

前面讲到进程:为了并发执行任务(程序),现代操作系统才引进进程的概念
分析:

  • 创建开销问题:创建一个进程开销:大

    • 子进程需要拷贝父进程的整个地址空间
  • 通信开销问题:进程间的通信

    • 需要用第三方(如:内核
  • P1 -> copy -> 内核 -> copy -> P2

  • 进程间通信代价或者开销也是很大的。进程的地址空间是独立,要通信的话需要用第三方的空

  • 间。

于是,就有人提出能不能在 同一个(同一个进程内部)进程地址空间中进行任务的并发:线程/轻量级
进程

线程是一个比进程更小的活动单位。它是进程中的执行路径(执行分支),线程也是并发的一种形式。
进程内部可以存在多个线程,它并发执行,但是进程内部的所有的线程共享整个进程的地址空间
main 函数:进程的主线程

1 线程特点

  • 创建一个线程要比创建进程开销要小很多

    • 因为创建一个线程的话,不需要拷贝进程的地址空间
  • 实现线程间的通信会更加方便

    • 因为进程内部所有线程共享整个进程地址空间
  • 线程也是一个动态概念:

    • 线程(进程)状态图
      • 就绪态:ready
      • 运行态:running
      • 阻塞态:blocking

有了线程的概念之后

  • 系统的调度单位就从进程变为线程,资源的分配还是以进程为单位

线程是进程内部的一个指令的执行分支,多个线程就是多个指令序列并发执行。这些指令必须在函数
内部,线程的指令部分肯定是封装一个函数的内部的。这个函数,就称之为:线程函数,一个线程在创
建之后,要执行的指令全部封装在该函数内部,这个线程函数执行完毕之后,该线程的任务也就执行完

2 线程函数的原型

typedef void *(*start_routine_t)(void *);
 // 函数指针:指向一个返回值为void*且带有一个void*参数的函数
// 类型重定义:将一个函数指针类型重命名为start_routine_t
 void *my_thread(void *) // 咱自定义的线程函数:必须要符合返回值是void*且带有void*参数的一
//个函数
{
    // 执行线程需要执行的代码
}

3.Linux对线程的API的支持

Linux下采用的是POSIX Thread线程库。简称为:pthread

示例

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
 string str = "你好,线程";
// 线程函数,表示线程启动之后第一时间会执行的函数
void *mythread(void *arg)
{
    cout  << "子线程:"  << str << endl;
    return nullptr;
}

int main()
{
  
    // 创建线程
    // 第一次参数,用来存储线程的id号
    pthread_t tid;
    pthread_create(&tid, nullptr, mythread, nullptr);
    // 子线程先指向父线程再打印 保证子线程在父线程后面结束
    sleep(1);
    cout << "主线程:" << str << endl;
    return 0;
}

3.1 创建一个线程

pthread_create:创建一个线程(启动一个线程)

 PTHREAD_CREATE(3)                                  Linux Programmer's Manual     
                             PTHREAD_CREATE(3)
 NAME
       pthread_create - create a new thread
 SYNOPSIS
       #include <pthread.h>
       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *
 (*start_routine) (void *), void *arg);
       /*
            @描述:
                创建一个新线程,启动一个线程
            @thread:
                指向的空间,用来存储线程的id号
            @attr:
                线程属性:
                    用于指定新创建的线程的一些属性的。
                    一般采用NULL,为默认属性
            @start_routine:
                线程函数,表示线程启动之后第一时间会执行的函数。
                也就是说新线程创建之后会指向start_routine函数内任务。
            @arg:
                参数,表示新线程要去start_routine执行任务,但是start_routine是一个函数。
start_routine是函数就可以有参数。所有arg实际上就是传给start_routine的参数的。
            @return:
                成功返回0,失败返回-1,同时errno被设置。
    
       */
 /*
 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *
 (*start_routine) (void *), void *arg)
 {
    // 其他的代码
    // 创建线程去执行start_routine
    start_routine(arg);
    
    如果线程创建成功
    return 0;
    
    否则就是:
    return -1;
 }
 */

示例

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
 string str = "你好,线程";
// 线程函数,表示线程启动之后第一时间会执行的函数
void *mythread(void *arg)
{
    cout  << "子线程:"  << str << endl;
    return nullptr;
}

int main()
{
  
    // 创建线程
    // 第一次参数,用来存储线程的id号
    pthread_t tid;
    pthread_create(&tid, nullptr, mythread, nullptr);
    // 子线程先指向父线程再打印 保证子线程在父线程后面结束
    sleep(1);
    cout << "主线程:" << str << endl;
    return 0;
}

3.2 线程的退出

线程函数的退出(线程函数的返回)

 void *start_routine(void *arg)
 {
    return nullptr; // 线程结束
}

在线程执行的任意时刻调用pthread_exit

 PTHREAD_EXIT(3)                                    Linux Programmer's Manual     
                               PTHREAD_EXIT(3)
 NAME
       pthread_exit - terminate calling thread
 SYNOPSIS
       #include <pthread.h>
       void pthread_exit(void *retval);
       /*
            @描述
                立即结束线程
            @retval:
                线程结束之后需要返回的参数,返回值的指针。
       */

被别人干掉

  • cancel:被别人取消(其他线程调用pthread_cancel)

    • t1:pthread_cancel(t2)

    • t1调用取消函数,取消t2,t2不一定会被取消

      • 因为t2能不能被其他线程取消,取决于t2线程的一个属性:取消属性

      • 它是否可以被cancelled

 PTHREAD_CANCEL(3)                                  Linux Programmer's Manual     
                             PTHREAD_CANCEL(3)
 NAMESYNOPSIS
       #include <pthread.h>
       int pthread_cancel(pthread_t thread);
       /*
            @描述:
                取消一个指定的线程
            @thread:
                线程号,需要取消的那个线程的id号
            @return:
                成功返回0,失败返回非0.
       */
       pthread_cancel - send a cancellation request to a thread
SYNOPSIS
       #include <pthread.h>
       int pthread_cancel(pthread_t thread);
       /*
            @描述:
                取消一个指定的线程
            @thread:
                线程号,需要取消的那个线程的id号
            @return:
                成功返回0,失败返回非0.
       */
  • 这个属性叫做:可以被取消属性
    • PTHREAD_CANCEL_ENABLE:表示该线程可以被取消
    • PTHREAD_CANCEL_DISABLE:表示该线程不能被取消
 PTHREAD_SETCANCELSTATE(3)                          Linux Programmer's Manual     
                     PTHREAD_SETCANCELSTATE(3)
 NAME
       pthread_setcancelstate, pthread_setcanceltype - set cancelability state 
and type
 SYNOPSIS
       #include <pthread.h>
       int pthread_setcancelstate(int state, int *oldstate);
       /*
            @描述:
                设置线程取消属性的
            @state: 
                设置线程的取消状态
                    PTHREAD_CANCEL_ENABLE:表示该线程可以被取消
                    PTHREAD_CANCEL_DISABLE:表示该线程不能被取消
            @oldstate:
                线程上一次的取消状态
            @return:
                成功返回0,失败返回其他值
       */  

一个线程退出了,并不是所有资源都会释放。一个线程的退出,它资源释放全部被释放,取决于一个属 性

示例

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
 string str = "你好,线程";
// 线程函数,表示线程启动之后第一时间会执行的函数
void *mythread(void *arg)
{
    //  存储的是调用前的状态
    int old_state = 0;
    // 控制当前线程的可取消性 PTHREAD_CANCEL_DISABLE不能被取消 |PTHREAD_CANCEL_ENABLE可以被取消
    // pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,& old_state);
    cout << (old_state == PTHREAD_CANCEL_DISABLE? "不可以被取消":"可以被取消") << endl;
    while (1)
    {
        cout  << "子线程:"  << str << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
  
    // 创建线程
    // 第一次参数,用来存储线程的id号
    pthread_t tid;
    pthread_create(&tid, nullptr, mythread, nullptr);
    // 子线程先指向父线程再打印 保证子线程在父线程后面结束
    sleep(3);
    cout << "主线程:" << str << endl;
    pthread_cancel(tid);
     sleep(3);
    return 0;
}

3.3 资源分离

detach:分离属性:

  • ENABLE: 分离资源属性

    • 该线程结束,它的所有资源都会自动释放。
  • DISABLE: 不分离资源

    • 该线程结束,会有部分资源不会自动释放,需要其他线程调用pthread_join这个函数才能完

    全释放

3.3.1 线程资源回收函数
PTHREAD_JOIN(3)                                                   Linux 
Programmer's Manual                                                   
PTHREAD_JOIN(3)
 NAME
       pthread_join - join with a terminated thread
 SYNOPSIS
       #include <pthread.h>
       int pthread_join(pthread_t thread, void **retval);
       /*
            @描述:
                等待一个指定的线程结束 阻塞状态
            @thread:
                需要等待的线程id号
            @retval:
                二级指针,表示线程函数的返回值指针
            @return:
                成功返回0
                失败返回-1
       */
3.3.2 资源分类属性
 PTHREAD_DETACH(3)                                                 Linux 
Programmer's Manual                                                 
PTHREAD_DETACH(3)
 NAME
       pthread_detach - detach a thread
 SYNOPSIS
       #include <pthread.h>
       int pthread_detach(pthread_t thread);
       /*
            @描述:
                设置线程的资源分离属性
            @thread:
                需要设置资源分离属性的那个线程id
            @return:
                成功返回0
                失败返回-1
         */
3.3.3 获取自身ID号
#include <pthread.h>
 pthread_t pthread_self(void);
 /*
作用:
获取当前所在线程的tid号
@return:
返回当前所在线程的tid号
*/

示例

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
 string str = "你好,线程";
// 线程函数,表示线程启动之后第一时间会执行的函数
void *mythread(void *arg)
{
   pthread_detach(pthread_self());
    while (1)
    {
        cout  << "子线程:"  << str << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
  
    // 创建线程
    // 第一次参数,用来存储线程的id号
    pthread_t tid;
    pthread_create(&tid, nullptr, mythread, nullptr);
    
    cout << "主线程:" << str << endl;
    // 取消线程   
    pthread_cancel(tid);
    sleep(3);
    // 资源回收
    pthread_join(tid,nullptr);
    return 0;
}

4. 线程的同步/互斥机制

为了线程之间,能够去有序的访问共享资源,引用信号量机制

  • 信号量System VPOSIX 信号量
  • 线程互斥锁

4.1 线程互斥锁

线程互斥锁也是信号量,只不过线程互斥锁,存在于进程地址空间,用于线程间同步和互斥操作,线程
互斥锁它的效率相对信号量来说要高。
线程互斥锁:使用pthread_mutex_t 的类型来描述一个锁
安装线程POSIX 帮助手册:sudo apt-get install manpages-posix-dev // 安装posix 帮助手册

  • 初始化线程互斥锁
PTHREAD_MUTEX_INIT(3POSIX)                                             POSIX Programmer's 
Manual                             					PTHREAD_MUTEX_INIT(3POSIX)

 PROLOG
		 This  manual  page  is part of the POSIX Programmer's Manual.  The Linux 
implementation of this interface may differ (consult the
         corresponding Linux manual page for details of Linux behavior), or the 
interface may not be implemented on Linux.
 NAME
 		pthread_mutex_init — destroy and initialize a mutex
 SYNOPSIS
 		#include <pthread.h>
 		int pthread_mutex_init(pthread_mutex_t *restrict mutex,const 
pthread_mutexattr_t *restrict attr);
 /*
 		@描述:
			初始一个线程互斥锁
		@mutex:
			需要初始化的线程互斥锁地址
			
			pthread_mutex_t mutex; // 创建了一个互斥锁	
  			&mutex ---> 互斥锁地址
                
         @attr:
             线程互斥锁的属性,一般为NULL,采用默认属性
             如:
                  线程的互斥锁默认设置 1 unlock
                    
            @return:
                成功返回0,失败返回-1
       */
  • 线程互斥锁的PV操作
int pthread_mutex_lock(pthread_mutex_t *mutex);
 /*
    作用:
        死等上锁,如果该锁没有被释放,则会一直阻塞在此函数,等待该锁被释放。
    @mutex:
        需要上锁的互斥锁指针
    @return:
        成功返回0,表示获取到了该互斥锁
        返回-1,表示获取出错,没有获取到互斥锁
*/
 int pthread_mutex_trylock(pthread_mutex_t *mutex);
 /*
    作用: 
        尝试上锁,尝试性上锁,如果该锁没被释放,那么立即返回执行后面的代码。
    @mutex:
        需要上锁的互斥锁指针
    @return:
        成功返回0,表示获取到了该互斥锁
        其他值,表示没有获取到互斥锁
*/
 int pthread_mutex_timedlock(pthread_mutex_t *mutex,struct timespec 
*abs_timeout);
*abs_timeout);
 /*
    作用:
        限时上锁,如果该锁没有被释放,则会一直阻塞在此函数中一段实际,等待该锁被释放。如果时间
过了还没有被释放,那么果断放弃执行后面的代码。
        
    @mutex:
        需要上锁的互斥锁指针
    @abs_timeout:
        绝对时间(超时时间),上锁的时间范围。
    @return:
        成功返回0,表示获取到了该互斥锁
        其他值,表示没有获取到互斥锁
        
*/
  • V操作(解锁操作)
int pthread_mutex_unlock(pthread_mutex_t *mutex);
 /*
作用:
解锁线程互斥锁
@mutex:
需要解锁的线程互斥锁指针
*/
  • 线程互斥锁的销毁操作
int pthread_mutex_destroy(pthread_mutex_t *mutex);
 /*
	作用:
		销毁一个线程互斥锁
	@mutex:
		需要销毁的线程互斥锁指针
	@return:
		成功返回0,失败返回-1
 */

5.生产者消费者模型

生产者消费者模型:利用厂商和消费者关系,由生产者线程进行生产(产生任务),再由消费者线程消 费(执行任务),没有任务的时候,消费者等待生产者产生任务。

  • 共享资源的互斥访问问题
    • 信号量/线程互斥锁

当缓冲区(生产者没有产出的时候)没有数据的时候,(消费者)应该怎么办?

  • 不停的去测试,看有没有数据。

    • 轮询访问,但是轮询有缺陷:一直在访问,浪费CPU资源。轮询有时间差,占用总线:`Is ``

      always busy

  • 让出CPU,当有数据的时候,再唤醒我(wake up),线程条件变量:同步

5.1 线程条件变量

线程条件变量:在多线程程序设计中,可以用条件变量为表示一个特定的条件或者是事件
pthread_cond_t :来描述一个条件变量(类型)
至于条件变量,到底是一个什么事件或者说表示一个什么条件?完全由程序猿去解释这个条件变量所代
表的含义。
在条件变量上的三种操作:

  • 初始化
  • 等待一个条件变量(等待该条件变量所表示的事件)
  • 唤醒一个线程/触发条件变量(唤醒了正在等待该事件的线程)
int data = 0;
 main : 主线程:生产者:包工头
	data = 1;
 t1:子线程:消费者:牛马
	when data == 1;
		干活
		data = 0; 

5.2 线程条件变量 API

5.2.1 初始化/销毁条件变量
PTHREAD_COND_DESTROY(3POSIX)                                      		POSIX 
Programmer's Manual                                      
PTHREAD_COND_DESTROY(3POSIX)
		
 PROLOG
 		This  manual  page  is  part of the POSIX Programmer's Manual.  The Linux 
implementation of this interface may differ (consult the corresponding Linux
 		manual page for details of Linux behavior), or the interface may not be 
implemented on Linux.
 NAME
 		pthread_cond_destroy, pthread_cond_init — destroy and initialize 
condition variables
 
SYNOPSIS
 		#include <pthread.h>
 		
 		int pthread_cond_destroy(pthread_cond_t *cond);
 /*
			 @描述:
				销毁一个条件变量
			@cond:
				需要销毁条件变量指针
*/
  int pthread_cond_init(pthread_cond_t *restrict cond,const 
pthread_condattr_t *restrict attr);
        /*
            @描述:
                初始化一个条件变量
            @cond:
                需要初始化的条件变量的指针
            @attr:
                初始化的条件变量的属性,一般为NULL,采用默认设置
            @return:
                成功返回0,失败返回其他值
        
        */
5.2.2 等待一个条件变量
 PTHREAD_COND_TIMEDWAIT(3POSIX)                                    POSIX 
Programmer's Manual                                    
PTHREAD_COND_TIMEDWAIT(3POSIX)
 PROLOG
       This  manual  page  is  part of the POSIX Programmer's Manual.  The Linux 
implementation of this interface may differ (consult the corresponding Linux
       manual page for details of Linux behavior), or the interface may not be 
implemented on Linux.
 NAME
       pthread_cond_timedwait, pthread_cond_wait — wait on a condition
 SYNOPSIS
       #include <pthread.h>
       int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t 
*restrict mutex,
           const struct timespec *restrict abstime);
       /*
            @描述:
                限时等待条件变量
            @cond:
                需要等待的那个条件变量指针
            @mutex:
                线程互斥锁:为了保护cond所表示的那个事件/共享资源的。
                条件变量实际也是一个共享资源。
            @abstime:
                绝对时间,需要被唤醒的绝对时间
       */
           
       int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t 
*restrict mutex);
        /*
            @描述:
                等待那个条件变量
            @cond:
                需要等待的那个条件变量指针
            @mutex:
                线程互斥锁:为了保护cond所表示的那个事件/共享资源的。
                条件变量实际也是一个共享资源。
            @abstime:
                绝对时间,需要被唤醒的绝对时间
                  */
           
       int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t 
*restrict mutex);
        /*
            @描述:
                等待那个条件变量
            @cond:
                需要等待的那个条件变量指针
            @mutex:
                线程互斥锁:为了保护cond所表示的那个事件/共享资源的。
                条件变量实际也是一个共享资源。
                  mutex:locked 上锁
                pthread_cond_wait()
                {
                    ... 准备工作
                    mutex:unlock 解锁
                    让出CPU 等待
                    ...
                    when 当条件产生的时候,其他的线程唤醒我的时候
                        mutex:locked 上锁
                }
            @return:
                成功返回0,被其他线程唤醒
                失败返回其他值
        
        */
5.3.3 唤醒线程/触发条件变量
PTHREAD_COND_BROADCAST(3POSIX)                                    POSIX 
Programmer's Manual                                    
PTHREAD_COND_BROADCAST(3POSIX)
 PROLOG
       This  manual  page  is  part of the POSIX Programmer's Manual.  The Linux 
implementation of this interface may differ (consult the corresponding Linux
       manual page for details of Linux behavior), or the interface may not be 
implemented on Linux.
 NAME
       pthread_cond_broadcast, pthread_cond_signal — broadcast or signal a 
condition
 SYNOPSIS
       #include <pthread.h>
        // 广播唤醒
       int pthread_cond_broadcast(pthread_cond_t *cond);
       /*
            @描述:
                唤醒所有正在等待的线程
            @cond:
                那个条件变量
            @return:
                成功返回0,失败返回其他值
       
       */
       // 单个唤醒
       int pthread_cond_signal(pthread_cond_t *cond);
       /*
            @描述:
                只唤醒一个线程在等待的线程。
            @cond:
                那个条件变量
            @return:
                成功返回0,失败返回其他值
                */

注意:广播唤醒和单个唤醒的区别

  • 广播唤醒:唤醒所有等待的线程,去执行任务,但是任务可能不够分,那么没分到的线程继续休眠
  • 单个唤醒:随机唤醒一个线程执行任务,其他线程继续休眠。
#include <iostream>
#include <vector>
#include <unistd.h>
#include <pthread.h>

// 全局变量供父子线程使用
int a = 0;
int b = 0;
// 线程互斥锁
pthread_mutex_t mutex;
// 条件变量
pthread_cond_t cond;
void *myrhtread(void *arg)
{
    usleep(10);
    std::cout << "子线程:" << pthread_self() << "启动完成" << std::endl;
    while (1)
    {
           // 上锁
        pthread_mutex_lock(&mutex);
        // 休息,等待主线程给活,没有就休息等待
        pthread_cond_wait(&cond, &mutex);
     
         //
        if (a < 0 && b < 0)
        {
            //小于0时候退先解锁在退出
            pthread_mutex_unlock(&mutex);
            break;
        }
        std::cout << "子线程:" << pthread_self()  << "计算结果是:" << a + b << std::endl;
        // 解锁
        pthread_mutex_unlock(&mutex);
    }
  return nullptr;
}
int main()
{
    // 初始化互斥锁
    pthread_mutex_init(&mutex, nullptr);
    // 初始化条件变量
    pthread_cond_init(&cond, nullptr);
    // 定义五个工作者(牛马)
    std::vector<pthread_t> works;
    // 创建线程
    for (int i = 0; i < 5; i++)
    {
        pthread_t tid;
        pthread_create(&tid, nullptr, myrhtread, nullptr);
        // 添加到容器里面
        works.push_back(tid);
    }
        usleep(10);
    // 生产者
    while (1)
    {
        // 上锁
        pthread_mutex_lock(&mutex);
        
        std::cout << "请输入" << std::endl;
        std::cin >> a >> b;
        // 退出条件
        if(a < 0 && b < 0)
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
        // 解锁
        pthread_mutex_unlock(&mutex);
       
       // 唤醒线程可以工作了
        pthread_cond_signal(&cond);
        usleep(10);
    }
    // 唤醒全部线程逐步退出
    pthread_cond_broadcast(&cond);

     // 等待工作者线程结束
    for(pthread_t tid:works)
    {
        pthread_join(tid, nullptr);
    }
    // 销毁条件变量
    pthread_cond_destroy(&cond);
    // 销毁锁
    pthread_mutex_destroy(&mutex);
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2395705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

apptrace 的优势以及对 App 的价值

官网地址&#xff1a;AppTrace - 专业的移动应用推广追踪平台 apptrace 的优势以及对 App 的价值​ App 拉起作为移动端深度链接技术的关键应用&#xff0c;能实现从 H5 网页到 App 的无缝跳转&#xff0c;并精准定位到 App 内指定页面。apptrace 凭借专业的技术与丰富的经验…

android studio debug调试出现 IOException异常

解决Android调试端口无法打开的问题&#xff0c;出现"Unable to open debugger port"错误时&#xff0c;可以进入app设置&#xff0c;选择Debugger选项&#xff0c;将Debug type更改为Java Only模式。这个方法适用于Android Studio调试时遇到的端口连接问题&#xff…

vr中风--数据处理模型搭建与训练

# -*- coding: utf-8 -*- """ MUSED-I康复评估系统&#xff08;增强版&#xff09; 包含&#xff1a;多通道sEMG数据增强、混合模型架构、标准化处理 """ import numpy as np import pandas as pd from sklearn.model_selection import train_te…

前端学习(7)—— HTML + CSS实现博客系统页面

目录 一&#xff0c;效果展示 二&#xff0c;实现博客列表页 2.1 实现导航栏 2.2 实现个人信息 2.3 实现博客列表 三&#xff0c;实现博客正文页 3.2 复用 3.4 实现博客正文 四&#xff0c;实现博客登录页 4.1 版心 4.2 登录框 五&#xff0c;实现博客编辑页 5.1 …

RuoYi前后端分离框架实现前后端数据传输加密(二)之前端篇

一、背景 本文是RuoYi前后端分离框架实现前后端数据传输加密(一)之后端篇文章配套的,主要介绍前端对自定义字段传输加密的实现,两篇文章结合可以完整的完成RuoYi前后端分离框架对API通信过程中实现自定义字段加密传输。前端的加解密实现,不涉及到界面的修改,仅仅是方法的…

基于视觉的车道线检测完整代码:让驾驶更安全的开源解决方案

基于视觉的车道线检测完整代码&#xff1a;让驾驶更安全的开源解决方案 【下载地址】基于视觉的车道线检测完整代码 这是一个基于视觉的车道线检测开源项目&#xff0c;提供完整的代码示例&#xff0c;采用滑动窗口算法实现。项目通过逐行扫描图像&#xff0c;精准识别曲线车道…

鸿蒙仓颉开发语言实战教程:自定义tabbar

大家周末好呀&#xff0c;今天继续分享仓颉语言开发商城应用的实战教程&#xff0c;今天要做的是tabbar。 大家都知道ArkTs有Tabs和TabContent容器&#xff0c;能够实现上图的样式&#xff0c;满足基本的使用需求。而仓颉就不同了&#xff0c;它虽然也有这两个组件&#xff0c;…

28 C 语言作用域详解:作用域特性(全局、局部、块级)、应用场景、注意事项

1 作用域简介 作用域定义了代码中标识符&#xff08;如变量、常量、数组、函数等&#xff09;的可见性与可访问范围&#xff0c;即标识符在程序的哪些位置能够被引用或访问。在 C 语言中&#xff0c;作用域主要分为三类&#xff1a; 全局作用域局部作用域块级作用域 需注意&am…

MySQL 事务解析

1. 事务简介 事务&#xff08;Transaction&#xff09; 是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 经典案例&#xff1…

题海拾贝:压缩字符串

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞&#xff0c;关注&am…

振动力学的三类基本问题

振动问题的分类依赖于分类的出发点&#xff0c;本文从系统论的角度来分析振动问题的分类。如图1&#xff0c;一个振动系统&#xff0c;包括三个方面&#xff1a;输入、系统特性&#xff08;或称为系统模型&#xff09;、输出。其中&#xff0c;输入指外界载荷&#xff0c;包括力…

移动端 UI自动化测试学习之Appium框架(包含adb调试工具介绍)

文章目录 前言adb调试工具adb组成常用命令获取程序的包名和界面名文件传输发送文件到手机从手机中拉取文件 获取app启动时间获取手机日志其他命令 Appium 简介工作原理图 环境搭建安装客户端库&#xff08;appium lib&#xff09;安装Appium Server安装JDK&#xff08;自行下载…

CS144 - Lecture 2

CS144 - Lecture 1 TCP 这里就简单讲了一下它的基本性质&#xff0c;没啥好说的 UDP 提供不可靠的传输服务&#xff0c;我们的 DNS 服务和 DHCP 都是用的 UDP 协议。 对于 DNS 我们只是单纯地向 DNS 服务器发送域名&#xff0c;然后返回一个 IP&#xff0c;如果还需要建立…

B站视频下载器 v1.0.4|免登录下载1080P视频

核心亮点 ✅ 无需登录下载1080P高清视频✅ 支持Windows/macOS双平台✅ 纯净无广告完全免费✅ 可单独下载视频/音频/弹幕/字幕/封面 三步极简操作 粘贴B站视频链接选择保存位置点击「开始下载」 特色功能 独立下载选项&#xff08;视频/音频/弹幕/字幕/封面&#xff09;登录…

AIGC学习笔记(8)——AI大模型开发工程师

文章目录 AI大模型开发工程师007 LangChain之Model IO模块1 Model IO核心概念2 Model IO代码实战什么是LCEL&#xff1f;ModelModel的分类LLMsChatModel PromptPrompt templatesExample selectorsOutput parsers AI大模型开发工程师 007 LangChain之Model IO模块 1 Model IO核…

[蓝桥杯]剪格子

剪格子 题目描述 如下图所示&#xff0c;3 x 3 的格子中填写了一些整数。 我们沿着图中的红色线剪开&#xff0c;得到两个部分&#xff0c;每个部分的数字和都是 60。 本题的要求就是请你编程判定&#xff1a;对给定的 mnmn 的格子中的整数&#xff0c;是否可以分割为两个部…

明远智睿SSD2351开发板:语音机器人领域的变革力量

在人工智能快速发展的今天&#xff0c;语音机器人逐渐成为人们生活和工作中的得力助手。明远智睿SSD2351开发板凭借强大性能与丰富功能&#xff0c;为语音机器人的发展注入新动力&#xff0c;成为该领域的变革力量。 SSD2351开发板的四核1.4GHz处理器具备强劲的运算性能&#x…

Co-IP—验证蛋白互作的不二之选

蛋白互作在细胞生命活动中起着至关重要的作用&#xff0c;并在不同的时空层面上参与多种细胞活动&#xff0c;因此研究蛋白互作对于理解分子调控网络至关重要。而在植物中筛选到潜在的互作蛋白后&#xff0c;大多数情况下&#xff0c;获得表达两种蛋白的稳定转化植株费时又费力…

数据可视化(第4、5、6次课)

Matplotlib 折线图 import numpy as np import matplotlib.pyplot as plt import matplotlib # 配置中文格式——保证图中出现中文的时候不会乱码 matplotlib.rcParams[font.sans-serif][SimHei] matplotlib.rcParams[axes.unicode_minus]False # 绘图 x np.linspace(0,2*np…

DAY 18 推断聚类后簇的类型

目录 DAY 18 推断聚类后簇的类型1.推断簇含义的2个思路&#xff1a;先选特征和后选特征2.通过可视化图形借助ai定义簇的含义3.科研逻辑闭环:通过精度判断特征工程价值作业&#xff1a;参考示例代码对心脏病数据集采取类似操作&#xff0c;并且评估特征工程后模型效果有无提升。…