详解linux多线程——互斥锁、条件变量、读写锁、自旋锁、信号量

news2025/1/4 15:28:05

一、互斥锁(同步)

  在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西,如果不做任何处理的话,打印出来的东西肯定是错乱的。

  在线程里也有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。

【互斥锁的特点】:

1. 原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;

2. 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;

3. 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。

【互斥锁的操作流程如下】:

1. 在访问共享资源后临界区域前,对互斥锁进行加锁;

2. 在访问完成后释放互斥锁导上的锁。在访问完成后释放互斥锁导上的锁;

3. 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

#include <pthread.h>
#include <time.h>
// 初始化一个互斥锁。
int pthread_mutex_init(pthread_mutex_t *mutex, 
						const pthread_mutexattr_t *attr);
// 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,
// 直到互斥锁解锁后再上锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);
// 调用该函数时,若互斥锁未加锁,则上锁,返回 0;
// 若互斥锁已加锁,则函数直接返回失败,即 EBUSY。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
// 当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock 互斥量
// 原语允许绑定线程阻塞时间。即非阻塞加锁互斥量。
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
const struct timespec *restrict abs_timeout);
// 对指定的互斥锁解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 销毁指定的一个互斥锁。互斥锁在使用完毕后,
// 必须要对互斥锁进行销毁,以释放资源。
int pthread_mutex_destroy(pthread_mutex_t *mutex);

【Demo】(阻塞模式):

//使用互斥量解决多线程抢占资源的问题
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
 
char* buf[5]; //字符指针数组  全局变量
int pos; //用于指定上面数组的下标
 
//1.定义互斥量
pthread_mutex_t mutex;
 
void *task(void *p)
{
    //3.使用互斥量进行加锁
    pthread_mutex_lock(&mutex);
 
    buf[pos] = (char *)p;
    sleep(1);
    pos++;
 
    //4.使用互斥量进行解锁
    pthread_mutex_unlock(&mutex);
}
 
int main(void)
{
    //2.初始化互斥量, 默认属性
    pthread_mutex_init(&mutex, NULL);
 
    //1.启动一个线程 向数组中存储内容
    pthread_t tid, tid2;
    pthread_create(&tid, NULL, task, (void *)"zhangfei");
    pthread_create(&tid2, NULL, task, (void *)"guanyu");
    //2.主线程进程等待,并且打印最终的结果
    pthread_join(tid, NULL);
    pthread_join(tid2, NULL);
 
    //5.销毁互斥量
    pthread_mutex_destroy(&mutex);
 
    int i = 0;
    printf("字符指针数组中的内容是:");
    for(i = 0; i < pos; ++i)
    {
        printf("%s ", buf[i]);
    }
    printf("\n");
    return 0;
}

【Demo】(非阻塞模式):

#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
 
int main (void)
{
    int err;
    struct timespec tout;
    struct tm *tmp;
    char buf[64];
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    
    pthread_mutex_lock (&lock);
    printf ("mutex is locked\n");
    clock_gettime (CLOCK_REALTIME, &tout);
    tmp = localtime (&tout.tv_sec); 
    strftime (buf, sizeof (buf), "%r", tmp);
    printf ("current time is %s\n", buf);
    tout.tv_sec += 10;
    err = pthread_mutex_timedlock (&lock, &tout);
    clock_gettime (CLOCK_REALTIME, &tout);
    tmp = localtime (&tout.tv_sec);
    strftime (buf, sizeof (buf), "%r", tmp);
    printf ("the time is now %s\n", buf);
    if (err == 0)
        printf ("mutex locked again\n");
    else 
        printf ("can`t lock mutex again:%s\n", strerror (err));
    return 0;
}

相关视频推荐

高并发场景下,三种锁方案:互斥锁,自旋锁,原子操作的优缺点
 

自旋锁、互斥锁、信号量、原子操作、条件变量在不同开源框架的应用
 

死锁的成因,死锁的检测方案


免费学习地址:c/c++ linux服务器开发/后台架构师
 

需要C/C++ Linux服务器架构师学习资料加qun812855908领取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

 二、条件变量(同步)

  与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直 到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

  条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作:

一个线程等待"条件变量的条件成立"而挂起;

另一个线程使 “条件成立”(给出条件成立信号)。

【原理】:

  条件的检测是在互斥锁的保护下进行的。线程在改变条件状态之前必须首先锁住互斥量。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量 可以被用来实现这两进程间的线程同步。

【条件变量的操作流程如下】:

1. 初始化:init()或者pthread_cond_tcond=PTHREAD_COND_INITIALIER;属性置为NULL;

2. 等待条件成立:pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真 timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait);

3. 激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)

4. 清除条件变量:destroy;无线程等待,否则返回EBUSY清除条件变量:destroy;无线程等待,否则返回EBUSY

#include <pthread.h>
// 初始化条件变量
int pthread_cond_init(pthread_cond_t *cond,
						pthread_condattr_t *cond_attr);
// 阻塞等待
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
// 超时等待
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,
						const timespec *abstime);
// 解除所有线程的阻塞
int pthread_cond_destroy(pthread_cond_t *cond);
// 至少唤醒一个等待该条件的线程
int pthread_cond_signal(pthread_cond_t *cond);
// 唤醒等待该条件的所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);  

1、线程的条件变量实例1

  Jack开着一辆出租车来到一个站点停车,看见没人就走了。过段时间,Susan来到站点准备乘车,但是没有来,于是就等着。过了一会Mike开着车来到了这个站点,Sunsan就上了Mike的车走了。如图所示:

 

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <pthread.h>  
  
pthread_cond_t taxicond = PTHREAD_COND_INITIALIZER;  
pthread_mutex_t taximutex = PTHREAD_MUTEX_INITIALIZER;  
  
void *traveler_arrive(void *name)  
{  
    char *p = (char *)name;  
  
    printf ("Travelr: %s need a taxi now!\n", p);  
    // 加锁,把信号量加入队列,释放信号量
    pthread_mutex_lock(&taximutex);  
    pthread_cond_wait(&taxicond, &taximutex);  
    pthread_mutex_unlock(&taximutex);  
    printf ("traveler: %s now got a taxi!\n", p);  
    pthread_exit(NULL);  
}  
  
void *taxi_arrive(void *name)  
{  
    char *p = (char *)name;  
    printf ("Taxi: %s arrives.\n", p);
    // 给线程或者条件发信号,一定要在改变条件状态后再给线程发信号
    pthread_cond_signal(&taxicond);  
    pthread_exit(NULL);  
}  
  
int main (int argc, char **argv)  
{  
    char *name;  
    pthread_t thread;  
    pthread_attr_t threadattr; // 线程属性 
    pthread_attr_init(&threadattr);  // 线程属性初始化
  
    // 创建三个线程
    name = "Jack";  
    pthread_create(&thread, &threadattr, taxi_arrive, (void *)name);  
    sleep(1);  
    name = "Susan";  
    pthread_create(&thread, &threadattr, traveler_arrive, (void *)name);  
    sleep(1);  
    name = "Mike";  
    pthread_create(&thread, &threadattr, taxi_arrive, (void *)name);  
    sleep(1);  
  
    return 0;  
}

2、线程的条件变量实例2

  Jack开着一辆出租车来到一个站点停车,看见没人就等着。过段时间,Susan来到站点准备乘车看见了Jack的出租车,于是就上去了。过了一会Mike开着车来到了这个站点,看见没人救等着。如图所示:

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 
int travelercount = 0;
pthread_cond_t taxicond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t taximutex = PTHREAD_MUTEX_INITIALIZER;
 
void *traveler_arrive(void *name)
{
    char *p = (char *)name;
 
    pthread_mutex_lock(&taximutex);
 
    printf ("traveler: %s need a taxi now!\n", p);
    travelercount++;
    pthread_cond_wait(&taxicond, &taximutex);
            
    pthread_mutex_unlock(&taximutex);
    printf ("traveler: %s now got a taxi!\n", p);
    pthread_exit(NULL);
}
 
void *taxi_arrive(void *name)
{
    char *p = (char *)name;
    printf ("Taxi: %s arrives.\n", p);
    for(;;)
    {
        if(travelercount)
        {
            pthread_cond_signal(&taxicond);
            travelercount--;
            break;
        }
    }
    pthread_exit(NULL);
}
 
int main (int argc, char **argv)
{
    char *name;
    pthread_t thread;
    pthread_attr_t threadattr;
    pthread_attr_init(&threadattr);
 
    name = "Jack";
    pthread_create(&thread, &threadattr, taxi_arrive, name);
    sleep(1);
    name = "Susan";
    pthread_create(&thread, &threadattr, traveler_arrive, name);
    sleep(3);
    name = "Mike";
    pthread_create(&thread, &threadattr, taxi_arrive, name);
    sleep(4);
 
    return 0;
}

3、虚假唤醒(spurious wakeup)

  虚假唤醒(spurious wakeup)在采用条件等待时:

while(条件不满足)
{  
   condition_wait(cond, mutex);  
}  
// 而不是:  
If( 条件不满足 )
{  
   Condition_wait(cond,mutex);  
}   

这是因为可能会存在虚假唤醒”spurious wakeup”的情况。

  也就是说,即使没有线程调用condition_signal, 原先调用condition_wait的函数也可能会返回。此时线程被唤醒了,但是条件并不满足,这个时候如果不对条件进行检查而往下执行,就可能会导致后续的处理出现错误。

  虚假唤醒在linux的多处理器系统中/在程序接收到信号时可能回发生。在Windows系统和JAVA虚拟机上也存在。在系统设计时应该可以避免虚假唤醒,但是这会影响条件变量的执行效率,而既然通过while循环就能避免虚假唤醒造成的错误,因此程序的逻辑就变成了while循环的情况。

四、读写锁(同步)

  读写锁与互斥量类似,不过读写锁允许更改的并行性,也叫共享互斥锁。互斥量要么是锁住状态,要么就是不加锁状态,而且一次只有一个线程可以对其加锁。读写锁可以有3种状态:读模式下加锁状态、写模式加锁状态、不加锁状态。

  一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁(允许多个线程读但只允许一个线程写)。

【读写锁的特点】:

如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作;

如果有其它线程写数据,则其它线程都不允许读、写操作。

【读写锁的规则】:

如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁;

如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁。

读写锁适合于对数据结构的读次数比写次数多得多的情况。

#include <pthread.h>
// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *rwlock, 
						const pthread_rwlockattr_t *attr); 
// 申请读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock ); 
// 申请写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock ); 
// 尝试以非阻塞的方式来在读写锁上获取写锁,
// 如果有任何的读者或写者持有该锁,则立即失败返回。
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 
// 解锁
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); 
// 销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

【Demo】:

// 一个使用读写锁来实现 4 个线程读写一段数据是实例。
// 在此示例程序中,共创建了 4 个线程,
// 其中两个线程用来写入数据,两个线程用来读取数据
#include <stdio.h>  
#include <unistd.h>  
#include <pthread.h>  
pthread_rwlock_t rwlock; //读写锁  
int num = 1;  
  
//读操作,其他线程允许读操作,却不允许写操作  
void *fun1(void *arg)  
{  
    while(1)  
    {  
        pthread_rwlock_rdlock(&rwlock);
        printf("read num first == %d\n", num);
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
}
  
//读操作,其他线程允许读操作,却不允许写操作  
void *fun2(void *arg)
{
    while(1)
    {
        pthread_rwlock_rdlock(&rwlock);
        printf("read num second == %d\n", num);
        pthread_rwlock_unlock(&rwlock);
        sleep(2);
    }
}
 
//写操作,其它线程都不允许读或写操作  
void *fun3(void *arg)
{
    while(1)
    {
        pthread_rwlock_wrlock(&rwlock);
        num++;
        printf("write thread first\n");
        pthread_rwlock_unlock(&rwlock);
        sleep(2);
    }
}
 
//写操作,其它线程都不允许读或写操作  
void *fun4(void *arg)
{
    while(1)
    {  
        pthread_rwlock_wrlock(&rwlock);  
        num++;  
        printf("write thread second\n");  
        pthread_rwlock_unlock(&rwlock);  
        sleep(1);  
    }  
}  
  
int main()  
{  
    pthread_t ptd1, ptd2, ptd3, ptd4;  
      
    pthread_rwlock_init(&rwlock, NULL);//初始化一个读写锁  
      
    //创建线程  
    pthread_create(&ptd1, NULL, fun1, NULL);  
    pthread_create(&ptd2, NULL, fun2, NULL);  
    pthread_create(&ptd3, NULL, fun3, NULL);  
    pthread_create(&ptd4, NULL, fun4, NULL);  
      
    //等待线程结束,回收其资源  
    pthread_join(ptd1, NULL);  
    pthread_join(ptd2, NULL);  
    pthread_join(ptd3, NULL);  
    pthread_join(ptd4, NULL);  
      
    pthread_rwlock_destroy(&rwlock);//销毁读写锁  
      
    return 0;  
}  

五、自旋锁(同步)

  自旋锁与互斥量功能一样,唯一一点不同的就是互斥量阻塞后休眠让出cpu,而自旋锁阻塞后不会让出cpu,会一直忙等待,直到得到锁。

  自旋锁在用户态使用的比较少,在内核使用的比较多!自旋锁的使用场景:锁的持有时间比较短,或者说小于2次上下文切换的时间。

  自旋锁在用户态的函数接口和互斥量一样,把pthread_mutex_xxx()中mutex换成spin,如:pthread_spin_init()。

六、信号量(同步与互斥)

  信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

  编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。

#include <semaphore.h>
// 初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
// 信号量 P 操作(减 1)
int sem_wait(sem_t *sem);
// 以非阻塞的方式来对信号量进行减 1 操作
int sem_trywait(sem_t *sem);
// 信号量 V 操作(加 1)
int sem_post(sem_t *sem);
// 获取信号量的值
int sem_getvalue(sem_t *sem, int *sval);
// 销毁信号量
int sem_destroy(sem_t *sem);

【信号量用于同步】:

 

// 信号量用于同步实例
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
 
sem_t sem_g,sem_p;   //定义两个信号量
char ch = 'a';
 
void *pthread_g(void *arg)  //此线程改变字符ch的值
{
    while(1)
    {
        sem_wait(&sem_g);
        ch++;
        sleep(1);
        sem_post(&sem_p);
    }
}
 
void *pthread_p(void *arg)  //此线程打印ch的值
{
    while(1)
    {
        sem_wait(&sem_p);
        printf("%c",ch);
        fflush(stdout);
        sem_post(&sem_g);
    }
}
 
int main(int argc, char *argv[])
{
    pthread_t tid1,tid2;
    sem_init(&sem_g, 0, 0); // 初始化信号量为0
    sem_init(&sem_p, 0, 1); // 初始化信号量为1
    
    // 创建两个线程
    pthread_create(&tid1, NULL, pthread_g, NULL);
    pthread_create(&tid2, NULL, pthread_p, NULL);
    
    // 回收线程
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    return 0;
}

【信号量用于互斥】:

 

// 信号量用于互斥实例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
 
sem_t sem; //信号量
 
void printer(char *str)
{
    sem_wait(&sem);//减一,p操作
    while(*str) // 输出字符串(如果不用互斥,此处可能会被其他线程入侵)
    {
        putchar(*str);  
        fflush(stdout);
        str++;
        sleep(1);
    }
    printf("\n");
    
    sem_post(&sem);//加一,v操作
}
 
void *thread_fun1(void *arg)
{
    char *str1 = "hello";
    printer(str1);
}
 
void *thread_fun2(void *arg)
{
    char *str2 = "world";
    printer(str2);
}
 
int main(void)
{
    pthread_t tid1, tid2;
    
    sem_init(&sem, 0, 1); //初始化信号量,初始值为 1
    
    //创建 2 个线程
    pthread_create(&tid1, NULL, thread_fun1, NULL);
    pthread_create(&tid2, NULL, thread_fun2, NULL);
    
    //等待线程结束,回收其资源
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL); 
    
    sem_destroy(&sem); //销毁信号量
    
    return 0;
}

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

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

相关文章

华为ADS 2.0智驾助力阿维塔成为智能电动汽车领军品牌

随着科技的不断发展和智能化的迅速崛起&#xff0c;智能电动汽车已经成为当今汽车行业的热点话题。在这一领域&#xff0c;中国汽车品牌阿维塔科技近日引人瞩目地成为了首批搭载华为ADS 2.0智驾系统的品牌。在2023年4月16日举行的华为智能汽车解决方案发布会上&#xff0c;阿维…

HCIA第二次笔记

目录 OSI/RM七层参考模型——开放式的系统互联参考模型 核心——分层 TCP/IP模型——TCP/IP协议簇 应用层 应用层协议 封装与解封装 传输层 TCP协议和UDP协议的区别 TCP的报文 TCP的三次握手 TCP的四次挥手 TCP的四种可靠传输机制 OSI/RM七层参考模型——开放式的系…

单链表经典面试题 (动图解析)

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

Lars bak

&#xff08;1&#xff09;先从smalltalk说起上回书《阿伦凯(Alan Kay)》咱们说到世界上第一个基于语言虚拟机的编程语言Smalltalk&#xff0c;以及它的创造者&#xff1a;Alan kay。今天我再沿着语言虚拟机这条路&#xff0c;再走向系统虚拟机《虚拟机&#xff1a;IBM-S/360-O…

WebServer项目(一)->计网知识补充

WebServer项目->计网知识补充1.网络结构模式C/S结构B/S结构2.MAC 地址3.IP 地址1)IP 地址编址方式2)A类IP地址3)B类IP地址4)C类IP地址5)D类IP地址(了解)6)特殊的网址7)子网掩码4.端口5.网络模型1)OSI 七层参考模型&#xff08;Open System Interconnection&#xff09;2)TCP…

测试开发岗 - 一面复盘

1. 什么是软件测试&#xff0c; 谈谈你对软件测试的了解 软件测试就是验证产品特性是否符合用户需求, 软件测试贯穿于软件的整个生命周期. >>> 那软件测试具体是什么呢 ? 就拿生活中的例子来说, 比如说我们去商场买衣服, 会有以下几个步骤 : 第一步: 我们会走进门店…

SSM版本个人博客系统实现

SSM版本的个人博客系统 文章目录SSM版本的个人博客系统统一的数据返回处理关于前端的一些问题实现注册功能实现登录的功能存储session获取用户的信息获取左侧的个人信息获取右侧的博客列表时间格式化删除操作注销功能&#xff08;退出登录&#xff09;查看文章的详情页排查问题…

机器人项目与产品开发

ROS&#xff08;Robot Operating System&#xff09; ROS&#xff08;Robot Operating System&#xff09;是一个开源的机器人操作系统&#xff0c;旨在为机器人软件开发提供一个通用的、模块化的、分布式的软件平台。ROS由加州大学伯克利分校机器人实验室开发&#xff0c;目前…

一图看懂 xlwings 模块:基于 BSD 协议在 Excel 中方便调用 Python 库(反之亦然), 资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 xlwings 模块&#xff1a;基于 BSD 协议在 Excel 中方便调用 Python 库&#xff08;反之亦然&#xff09;, 资料整理笔记&#xff08;大全&#xff09;摘要模块图类关系图模…

向量和矩阵的backward

向量&#xff1a; 有yw*x&#xff0c;取w、x分别如下且y得&#xff1a; x1 tc.tensor([[5],[6]], dtypetc.float32, requires_gradTrue) w tc.tensor([[10,20],[30,40]], dtypetc.float32, requires_gradTrue) y1 tc.mm(w, x1) y1: tensor([[170.],[390.]], grad_fn<M…

网络安全必学 SQL 注入

1.1 .Sql 注入攻击原理 SQL 注入漏洞可以说是在企业运营中会遇到的最具破坏性的漏洞之一&#xff0c;它也是目前被利用得最多的漏洞。要学会如何防御 SQL 注入&#xff0c;首先我们要学习它的原理。 针对 SQL 注入的攻击行为可描述为通过在用户可控参数中注入 SQL 语法&#…

LightGBM——提升机器算法详细介绍(附代码)

LightGBM——提升机器算法 前言 LightGBM是个快速的&#xff0c;分布式的&#xff0c;高性能的基于决策树算法的梯度提升框架。可用于排序&#xff0c;分类&#xff0c;回归以及很多其他的机器学习任务中。 在竞赛题中&#xff0c;我们知道XGBoost算法非常热门&#xff0c;它…

MySQL:安装 MySQL、Navicat、使用 Navicat 连接 MySQL

文章目录Day 01&#xff1a;一、概念1. 数据库 DB2. 数据库管理系统 DBMS3. MySQL二、安装 MySQL三、安装 Navicat Premium 16四、使用 Navicat 连接 MySQL注意&#xff1a;Day 01&#xff1a; 一、概念 1. 数据库 DB 数据库&#xff1a;DB (Database) 数据仓库&#xff0c;…

NumPy 秘籍中文第二版:四、将 NumPy 与世界的其他地方连接

原文&#xff1a;NumPy Cookbook - Second Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 在本章中&#xff0c;我们将介绍以下秘籍&#xff1a; 使用缓冲区协议使用数组接口与 MATLAB 和 Octave 交换数据安装 RPy2与 R 交互安装 JPype将 NumPy 数组发送到 J…

脑电信号分析

导读 EEG信号的分析过程是为了获得能够突出信号本身特定特性的值&#xff0c;从而对其进行表征。同时&#xff0c;也需要将所获得的值通过准确的绘图技术来进行正确地显示&#xff0c;以使这些值对用户有用且清晰易读。目前&#xff0c;已有许多不同的脑电信号分析和显示技术&…

MVCC

MVCC基本概念 当前读 当前读 : 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁. 对于我们日常的操作. 如 : select....lock in share mode(共享锁) , select * for update , update ,insert,delete(排他锁) 都是一种当前读. 快…

「Cpolar」使用Typecho搭建个人博客网站【内网穿透实现公网访问】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后端的开发语言A…

Spring学习小结

文章目录1 BeanFactory与ApplicationContext的关系2 Spring基础环境下&#xff0c;常用的三个ApplicationContext3 Spring开发中Bean的配置4 Bean的初始化和销毁方法配置5 Bean的实例化配置6 Bean的依赖注入之自动装配7 Spring 的 xml 标签&#xff08;默认、自定义&#xff09…

硬件语言Verilog HDL牛客刷题 day09 哲K部分

1.VL59 根据RTL图编写Verilog程序 1.题目&#xff1a; 根据以下RTL图&#xff0c;使用 Verilog HDL语言编写代码&#xff0c;实现相同的功能&#xff0c;并编写testbench验证功能 2.解题思路 2.1 了解D触发器的知识 &#xff08;在时钟是上升沿的时候&#xff0c; 输入是什么…

UE “体积”的简单介绍

目录 一、阻挡体积 二、摄像机阻挡体积 三、销毁Z体积 四、后期处理体积 一、阻挡体积 你可以在静态网格体上使用阻挡体积替代碰撞表面&#xff0c;比如建筑物墙壁。这可以增强场景的可预测性&#xff0c;因为物理对象不会与地面和墙壁上的凸起细节相互作用。它还能降低物理模…