并发编程---多线程不安全示例以及解决,多线程创建方式

news2025/2/13 10:33:30

文章目录

  • 并发
  • 并行
  • 多线程
  • 为什么需要多线程
  • 线程不安全示例
  • 并发出现问题的根源: 并发三要素
    • 可见性: CPU 缓存引起
    • 原子性:分时复用引起
    • 有序性: 重排序引起
  • 线程不安全示例的解决方法
    • 使用AtomicLong类
    • 使用synchronized 关键字
  • 改进代码避免不必要的延迟
    • join()方法
      • 为什么使用 `join()`?
    • CountDownLatch类
      • 基本原理:
      • 常用方法:
  • 通过实现Runnable接口完成线程
  • 通过继承Thread类完成线程
  • 通过实现Callable接口完成接口
    • 手动创建线程,**使用 `FutureTask` 来处理 `Callable` 任务**
    • 使用线程池


并发

并发指的是系统在同一时间段内处理多个任务的能力,但并不要求这些任务同时执行。它侧重于任务的 逻辑上的同时进行,即在某个时间段内,系统切换多个任务的执行,这样看起来就像是多个任务“同时”运行,但实际上是在有限的资源上交替执行。

并发的特点

  • 逻辑上的同时:多个任务在时间上交替进行,给人一种“同时”执行的错觉,但它们通常是在一个处理器上通过快速切换执行的。
  • 时间分片:系统通过操作系统的调度器(例如时间片轮转)让任务看起来是并行的。
  • 可能不是物理同时执行:在单核 CPU 上,多个任务并发执行,但它们并不会真正同时执行。它们通过时间片的轮流执行来实现“并发”。

并发的例子

  • 单核 CPU 上运行多个任务。任务 A 和任务 B 不会同时执行,但它们会交替执行,任务 A 一会儿执行,接着任务 B 执行。
  • 例如,一个网站需要处理多个用户的请求,尽管每次只有一个请求在处理,但多个请求是并发的,因为它们会在时间上交替被处理。

并行

并行是指多个任务 同时 在多个处理器(或多个核心)上执行。它是并发的一种特殊情况,但必须具备 多核处理器多处理器系统 的支持,允许任务真正地同时执行。

并行的特点

  • 物理上的同时执行:多个任务在多个处理器或处理器核心上同时执行,任务间不需要交替执行。
  • 需要多核或多 CPU:并行计算通常依赖于硬件支持,例如多核 CPU 或分布式计算系统。
  • 速度更快:由于任务真正同时执行,通常比并发要高效,尤其是对于计算密集型任务。

并行的例子

  • 多核 CPU 上的并行任务。任务 A 和任务 B 可以在不同的核心上同时执行,不需要交替执行。
  • 例如,图像处理程序可以将图像划分为多个部分,然后在不同的处理器核心上同时处理每个部分,从而大大加快处理速度。

多线程

多线程是并发编程的一种方式,指的是一个程序内部可以有多个线程,这些线程共享程序的资源(如内存、文件描述符等),并且可以并行或并发地执行。每个线程都是操作系统调度的基本单位。

多线程的特点

  • 多线程是实现并发的一种方式,具体指的是在同一个进程中,通过线程来执行多个任务。
  • 在支持多核处理器的机器上,线程可以在多个核心上同时执行,这就实现了真正的并行。
  • 线程之间共享内存,因此可以快速地交换信息,但也会带来线程安全的问题。

为什么需要多线程

众所周知,CPU、内存、I/O 设备的速度是有极大差异的,为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系结构、操作系统、编译程序都做出了贡献,主要体现为:

  • CPU 增加了缓存,以均衡与内存的速度差异;// 导致 可见性 问题
  • 操作系统增加了进程、线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异;// 导致 原子性 问题
  • 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。// 导致 有序性 问题

线程不安全示例

如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。

这段代码演示了 10000 个线程并发执行计算任务,每个线程计算从 0100 的累加和并将结果加到共享变量 ThreadLearn.result 中。操作结束之后它的值有可能小于 50500000。

package thread;

public class ThreadLearn {

    public static long result; // 多个线程共享的,用于存储最终的计算结果
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10000; i++) { // 通过for循环启动10000个线程,每个线程通过MyThread类来执行任务
            MyThread myThread = new MyThread(100); // 每个线程都会计算从0到100的和
            // 启动线程,让线程并发执行
            Thread thread = new Thread(myThread);
            thread.start(); // start() 会异步调用run()方法
        }
        Thread.sleep(10*1000);
        System.out.println(result);
    }
}

class MyThread implements Runnable {
    private int count;

    public MyThread(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        int sum = 0;
        for (int i = 0; i <= count; i++) {
            sum += i;
        }
        ThreadLearn.result += sum; // 将每个线程的sum累加到result变量中。
    }
}
49833400 // 结果总是小于50500000

并发出现问题的根源: 并发三要素

上述代码输出为什么不是 50500000? 并发出现问题的根源是什么?

可见性: CPU 缓存引起

可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到。

举个简单的例子,看下面这段代码:

//线程1执行的代码
int i = 0;
i = 10;
 
//线程2执行的代码
j = i;

假若执行线程 1 的是 CPU1,执行线程 2 的是 CPU2。由上面的分析可知,当线程 1 执行 i = 10 这句时,会先把 i 的初始值加载到 CPU1 的高速缓存中,然后赋值为 10,那么在 CPU1 的高速缓存当中 i 的值变为 10 了,却没有立即写入到主存当中。

此时线程 2 执行 j = i,它会先去主存读取 i 的值并 i 加载到 CPU2 的缓存当中,注意此时内存当中 i 的值还是 0,i 那么就会使得 j 的值为 0,而不是 10.

这就是可见性问题,线程 1 对变量 i 修改了之后,线程 2 没有立即看到线程 1 修改的值。

原子性:分时复用引起

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

举个简单的例子,看下面这段代码:

int i = 1;

// 线程1执行
i += 1;

// 线程2执行
i += 1;

这里需要注意的是:i += 1 需要三条 CPU 指令

  1. 将变量 i 从内存读取到 CPU 寄存器;
  2. 在 CPU 寄存器中执行 i + 1 操作;
  3. 将最后的结果 i 写入内存(缓存机制导致可能写入的是 CPU 缓存而不是内存)。

由于 CPU 分时复用(线程切换)的存在,线程 1 执行了第一条指令后,就切换到线程 2 执行,假如线程 2 执行了这三条指令后,再切换会线程 1 执行后续两条指令,将造成最后写到内存中的 i 值是 2 而不是 3。

有序性: 重排序引起

有序性:即程序执行的顺序按照代码的先后顺序执行。举个简单的例子,看下面这段代码:

int a = 1; // 指令1
int b = 2; // 指令2

编译器可能会将其重排序为:

int b = 2; // 指令2
int a = 1; // 指令1

如果这些变量是共享变量,且多个线程依赖它们的顺序,就会导致问题。

线程不安全示例的解决方法

使用AtomicLong类

package thread;

public class ThreadLearn {

	// AtomicLong 是 Java 提供的一个类,用于处理长整型(long)数据类型的原子操作
	public static AtomicLong result = new AtomicLong(0);
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10000; i++) { // 通过for循环启动10000个线程,每个线程通过MyThread类来执行任务
            MyThread myThread = new MyThread(100); // 每个线程都会计算从0到100的和
            // 启动线程,让线程并发执行
            Thread thread = new Thread(myThread);
            thread.start(); // start() 会异步调用run()方法
        }
        Thread.sleep(10*1000);
        System.out.println(result);
    }
}

class MyThread implements Runnable {
    private int count;

    public MyThread(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        int sum = 0;
        for (int i = 0; i <= count; i++) {
            sum += i;
        }
        ThreadLearn.result.addAndGet(sum);
        // AtomicLong 提供了原子操作 addAndGet(),这个方法将 sum 加到 result 上,并返回更新后的值。
    }
}
50500000

AtomicLong 的作用: 使用 AtomicLongaddAndGet(sum) 方法,能够保证在多线程环境下,对 result 的累加操作是 原子 的,即使多个线程同时执行相同操作,也能保证每次操作都是安全的,不会产生竞争条件。

使用synchronized 关键字

package thread;

public class ThreadLearn {

    public static long result; // 多个线程共享的,用于存储最终的计算结果
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10000; i++) { // 通过for循环启动10000个线程,每个线程通过MyThread类来执行任务
            MyThread myThread = new MyThread(100); // 每个线程都会计算从0到100的和
            // 启动线程,让线程并发执行
            Thread thread = new Thread(myThread);
            thread.start(); // start() 会异步调用run()方法
        }
        Thread.sleep(10*1000);
        System.out.println(result);
    }
}

class MyThread implements Runnable {
    private int count;

    public MyThread(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        int sum = 0;
        for (int i = 0; i <= count; i++) {
            sum += i;
        }
        synchronized (ThreadLearn.class) {
            // synchronized关键字来保证同一时刻只有一个线程可以更新result的值
            // 通过加锁ThreadLearn.class(即类锁),使得对 result 的修改是线程安全的。
            ThreadLearn.result += sum;
        }
    }
}

synchronized 关键字确保同一时刻只有一个线程能进入被 synchronized 修饰的代码块。当一个线程正在执行被加锁的代码时,其他线程会被阻塞,直到该线程执行完毕并释放锁。这就避免了多个线程在同一时刻访问共享资源的情况,确保了对共享资源的修改是有序的,避免了数据竞争。

在这个代码中的使用

synchronized (ThreadLearn.class) {
    ThreadLearn.result += sum;
}

加锁对象synchronized 关键字后面跟的 ThreadLearn.class 表示加锁的是 ThreadLearn 类的对象锁。也就是说,所有线程在执行这个同步代码块时,必须先获取到 ThreadLearn 类的锁,才能执行 result += sum 操作。

保证顺序执行:通过加锁,当多个线程同时执行 run() 方法时,只有一个线程能进入 synchronized 块并修改 result。其他线程必须等待该线程执行完毕并释放锁后,才能继续修改 result。这样就保证了 result 的更新操作不会被多个线程同时执行,避免了并发问题。

改进代码避免不必要的延迟

join()方法

使用 join() 方法来确保所有线程执行完毕后再继续执行主线程,可以避免通过 Thread.sleep() 等待线程执行的潜在问题。以下是改进后的代码,使用了 join() 来确保主线程等待所有子线程执行完毕后再输出最终结果。

package thread;

import java.util.concurrent.atomic.AtomicLong;

public class ThreadLearn {

    // 使用 AtomicLong 提供原子操作
    public static AtomicLong result = new AtomicLong(0);

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis(); // 计时器
        int threadCount = 10000;
        Thread[] threads = new Thread[threadCount]; // 创建线程数组

        // 创建并启动 10000 个线程
        for (int i = 0; i < threadCount; i++) {
            MyThread myThread = new MyThread(100);
            threads[i] = new Thread(myThread);
            threads[i].start(); // 启动线程
        }

        // 使用 join() 等待所有线程执行完成
        for (Thread thread : threads) {
            thread.join(); // 确保主线程等待所有子线程执行完毕
        }

        // 打印最终结果和执行时间
        System.out.println(result);
        System.out.println("耗时:" + (System.currentTimeMillis() - start));
    }
}

class MyThread implements Runnable {
    private int count;

    public MyThread(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        int sum = 0;
        for (int i = 0; i <= count; i++) {
            sum += i; // 计算 0 到 count 的和
        }
        ThreadLearn.result.addAndGet(sum);
    }
}

thread.join()join() 方法用于让主线程等待每个子线程执行完毕。主线程会依次调用 join(),直到所有的子线程执行完成。这样可以确保 result 输出的值是正确的。

为什么使用 join()

  • 等待所有线程完成join() 会让主线程等待当前线程完成。在这种情况下,主线程会等所有子线程执行完毕,避免提前输出结果。
  • 防止线程还没执行完毕时输出结果:如果没有使用 join(),主线程可能会在子线程执行之前就打印出结果,从而导致 result 的值不准确。

CountDownLatch类

package thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;

public class ThreadLearn {

    // 使用 AtomicLong 提供原子操作
    public static AtomicLong result = new AtomicLong(0);
    static int count = 10000;  // 线程数
    public static CountDownLatch countDownLatch = new CountDownLatch(count);  // 初始化计数器

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis(); // 计时器

        Thread[] threads = new Thread[count]; // 创建线程数组,大小为 count

        // 创建并启动 10000 个线程
        for (int i = 0; i < count; i++) {
            MyThread myThread = new MyThread(100);  // 每个线程都会计算 0 到 100 的和
            threads[i] = new Thread(myThread);
            threads[i].start();  // 启动线程
        }

        // 等待所有线程完成
        countDownLatch.await(); // 主线程会在这里等待,直到计数器为 0

        // 打印最终结果和执行时间
        System.out.println(result);
        System.out.println("耗时:" + (System.currentTimeMillis() - start));
    }
}

class MyThread implements Runnable {
    private int count;

    public MyThread(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        int sum = 0;
        // 计算 0 到 count 的和
        for (int i = 0; i <= count; i++) {
            sum += i;
        }

        // 使用 AtomicLong 提供的原子操作进行更新
        ThreadLearn.result.addAndGet(sum);  // 线程安全的累加

        // 完成任务,计数器减 1
        ThreadLearn.countDownLatch.countDown();
    }
}

基本原理:

CountDownLatch 通过一个计数器来控制线程的等待,计数器的初始值是一个正整数。每当一个线程完成了某项工作,它会调用 countDown() 方法将计数器减 1。当计数器值减到 0 时,所有等待的线程(调用了 await() 方法的线程)都会被唤醒。

常用方法:

  • countDown():将计数器的值减 1。当计数器值为 0 时,所有等待的线程会被唤醒。
  • await():使当前线程等待,直到计数器的值为 0。该方法会阻塞当前线程,直到其他线程调用 countDown() 并使计数器减为 0。

通过实现Runnable接口完成线程

package thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;

public class ThreadLearn {

    // 使用 AtomicLong 提供原子操作
    public static AtomicLong result = new AtomicLong(0);
    static int count = 10000;  // 线程数
    public static CountDownLatch countDownLatch = new CountDownLatch(count);  // 初始化计数器

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis(); // 计时器

        Thread[] threads = new Thread[count]; // 创建线程数组,大小为 count

        // 创建并启动 10000 个线程
        for (int i = 0; i < count; i++) {
            MyThread myThread = new MyThread(100);  // 每个线程都会计算 0 到 100 的和
            threads[i] = new Thread(myThread);
            threads[i].start();  // 启动线程
        }

        // 等待所有线程完成
        countDownLatch.await(); // 主线程会在这里等待,直到计数器为 0

        // 打印最终结果和执行时间
        System.out.println(result);
        System.out.println("耗时:" + (System.currentTimeMillis() - start));
    }
}

class MyThread implements Runnable {
    private int count;

    public MyThread(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        int sum = 0;
        // 计算 0 到 count 的和
        for (int i = 0; i <= count; i++) {
            sum += i;
        }

        // 使用 AtomicLong 提供的原子操作进行更新
        ThreadLearn.result.addAndGet(sum);  // 线程安全的累加

        // 完成任务,计数器减 1
        ThreadLearn.countDownLatch.countDown();
    }
}

通过继承Thread类完成线程

public class ThreadLearn {

    // 使用 AtomicLong 提供原子操作
    public static AtomicLong result = new AtomicLong(0);
    static int count = 10000;  // 线程数
    public static CountDownLatch countDownLatch = new CountDownLatch(count);  // 初始化计数器

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis(); // 计时器

        Thread[] threads = new Thread[count]; // 创建线程数组,大小为 count

        // 创建并启动 10000 个线程
        for (int i = 0; i < count; i++) {
            MyThread1 myThread = new MyThread1(100);  // 每个线程都会计算 0 到 100 的和
            threads[i] = new Thread(myThread);
            threads[i].start();  // 启动线程
        }

        // 等待所有线程完成
        countDownLatch.await(); // 主线程会在这里等待,直到计数器为 0

        // 打印最终结果和执行时间
        System.out.println(result);
        System.out.println("耗时:" + (System.currentTimeMillis() - start));
    }
}

class MyThread1 extends Thread {
    private long count;

    public MyThread1(int count) {
        this.count = count;
    }
    @Override
    public void run() {
        super.run();
        int sum = 0;
        // 计算 0 到 count 的和
        for (int i = 0; i <= count; i++) {
            sum += i;
        }

        // 使用 AtomicLong 提供的原子操作进行更新
        ThreadLearn.result.addAndGet(sum);  // 线程安全的累加

        // 完成任务,计数器减 1
        ThreadLearn.countDownLatch.countDown();
    }
}

通过实现Callable接口完成接口

手动创建线程,使用 FutureTask 来处理 Callable 任务

public class ThreadLearn {

    // 使用 AtomicLong 提供原子操作
    public static AtomicLong result = new AtomicLong(0);
    static int count = 10000;  // 线程数
    public static CountDownLatch countDownLatch = new CountDownLatch(count);  // 初始化计数器

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTask<Integer> future = new FutureTask<Integer>(new MyThread2(100));
        Thread thread = new Thread(future);
        thread.start();
        Integer o = future.get();
        System.out.println(o);
    }
}

class MyThread2 implements Callable<Integer> {
    private int count;

    public MyThread2(int count) {
        this.count = count;
    }
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        // 计算 0 到 count 的和
        for (int i = 0; i <= count; i++) {
            sum += i;
        }

        // 使用 AtomicLong 提供的原子操作进行更新
        ThreadLearn.result.addAndGet(sum);  // 线程安全的累加

        // 完成任务,计数器减 1
        ThreadLearn.countDownLatch.countDown();
        return sum;
    }
}

使用线程池

public class ThreadLearn {

    // 使用 AtomicLong 提供原子操作
    public static AtomicLong result = new AtomicLong(0);
    public static CountDownLatch countDownLatch = new CountDownLatch(count);  // 初始化计数器
    public static ExecutorService executor = Executors.newFixedThreadPool(10);
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Future<Integer> submit = executor.submit(new MyThread2(100));
        Integer i = submit.get();
        System.out.println(i);
    }
}

class MyThread2 implements Callable<Integer> {
    private int count;

    public MyThread2(int count) {
        this.count = count;
    }
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        // 计算 0 到 count 的和
        for (int i = 0; i <= count; i++) {
            sum += i;
        }

        // 使用 AtomicLong 提供的原子操作进行更新
        ThreadLearn.result.addAndGet(sum);  // 线程安全的累加

        // 完成任务,计数器减 1
        ThreadLearn.countDownLatch.countDown();
        return sum;
    }
}

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

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

相关文章

更加通用的Hexo多端部署原理及实现,适用于各种系统之间

本文推荐在作者的个人博客网站阅读&#xff1a;shenying.online 一、故事背景 故事发生在大学上学期间&#xff08;而不是寒假&#xff09;。上学期间&#xff0c;宿舍条件极其恶劣&#xff0c;半夜断电、空间狭小。我们大学垃圾条件使用游戏本的种种弊端被无限放大&#xff1…

5g基站测试要求和关键点

5G基站的测试要求涉及多个方面&#xff0c;以确保其性能、覆盖能力、稳定性和合规性。以下是5G基站测试的主要要求和关键点&#xff1a; 一、基础性能测试 射频&#xff08;RF&#xff09;性能测试 发射机性能&#xff1a;验证基站的发射功率、频率误差、调制质量&#xff08;E…

算法——搜索算法:原理、类型与实战应用

搜索算法&#xff1a;开启高效信息检索的钥匙 在信息爆炸的时代&#xff0c;搜索算法无疑是计算机科学领域中熠熠生辉的存在&#xff0c;它就像一把神奇的钥匙&#xff0c;为我们打开了高效信息检索的大门。无论是在日常生活中&#xff0c;还是在专业的工作场景里&#xff0c;…

【嵌入式Linux应用开发基础】open函数与close函数

目录 一、open函数 1.1. 函数原型 1.2 参数说明 1.3 返回值 1.4. 示例代码 二、close函数 2.1. 函数原型 2.2. 示例代码 三、关键注意事项 3.1. 资源管理与泄漏防范 3.2. 错误处理的严谨性 3.3. 标志&#xff08;flags&#xff09;与权限&#xff08;mode&#xff…

在实体机和wsl2中安装docker、使用GPU

正常使用docker和gpu&#xff0c;直接命令行安装dcoker和&#xff0c;nvidia-container-toolkit。区别在于&#xff0c;后者在于安装驱动已经cuda加速时存在系统上的差异。 1、安装gpu驱动 在实体机中&#xff0c;安装cuda加速包&#xff0c;我们直接安装 driver 和 cuda 即可…

Unity3D实现显示模型线框(shader)

系列文章目录 unity工具 文章目录 系列文章目录👉前言👉一、效果展示👉二、第一种方式👉二、第二种方式👉壁纸分享👉总结👉前言 在 Unity 中显示物体线框主要基于图形渲染管线和特定的渲染模式。 要显示物体的线框,通常有两种常见的方法:一种是利用内置的渲染…

VAD端到端系列梳理以及阅读

0. 简介 最近VAD v2论文出来了&#xff0c;又掀起了一波该系列模型的热点。我们先看一下蒋博的文章&#xff0c;然后再来看一下《VADv2: End-to-End Vectorized Autonomous Driving via Probabilistic Planning》这篇文章&#xff0c;代码目前还没开源&#xff0c;可以期待一波…

在vmd中如何渲染透明水分子

1.设置背景为白色 依次点击Graphics>>Colors... 2. 改变渲染模式 依次点击Display>>rendermode>>GLSL 3. 渲染水分子 选中水分子&#xff0c;显色方式改为ColorID, 编号10的颜色&#xff1b; 选择材质为GlassBubble; 绘图方式为QuickSurf. 若水盒子显示效…

MybatisPlus常用增删改查

记录下MybatisPlus的简单的增删改查 接口概述 Service和Mapper区别 Mapper简化了单表的sql操作步骤&#xff08;CRUD&#xff09;&#xff0c;而Serivce则是对Mapper的功能增强。 Service虽然加入了数据库的操作&#xff0c;但还是以业务功能为主&#xff0c;而更加复杂的SQL…

常用电路(过压保护、电流/电压采集)

过压保护电路 输入电压使用电源&#xff08;36V&#xff09;或者typec&#xff08;20V&#xff09;&#xff0c;需要过压保护电路处理输入再连接到CH224K&#xff0c;保证输入不高于最大获取电压20V MOS管导通条件为栅源极有压差&#xff0c;一般为5-10V 三极管导通条件为基极…

干部监督系统“三色”预警的构建与应用

在新时代背景下&#xff0c;强化干部监督、提升管理水平已成为推动国家治理体系和治理能力现代化的关键一环。干部监督系统“三色”预警机制作为一种创新的管理工具&#xff0c;通过智能化、可视化的手段&#xff0c;实现了对干部行为的高效管理。本文将详细探讨干部监督系统“…

Zabbix-Trigger中的time函数坑

问题描述 由于功能需求&#xff0c;需要限制trigger的报警时间&#xff0c;所以加了如下的报警限制 and (time()>010000 and time()<045959)但是事与愿违&#xff0c;报警的时间总是对不上 但是&#xff0c;Zabbix设置的时区就是北京时间&#xff0c;应该是没有问题的…

9 数据流图

9 数据流图 9.1数据平衡原则 子图缺少处理后的数据操作结果返回前端应用以及后端数据库返回操作结果到数据管理中间件。 9.2解题技巧 实件名 存储名 加工名 数据流

python项目相关

遇到的问题 解决 Python 模块导入路径问题 问题描述 在运行 Python 文件时&#xff0c;可能会遇到以下错误&#xff1a; ModuleNotFoundError: No module named utils原因&#xff1a; Python 的模块导入机制依赖于当前工作目录和 sys.path 中的路径。当直接运行某个文件时…

基于轨道角动量自由度在空间频域中的可选择特性

将光的轨道角动量自由度应用到全息领域&#xff0c;证实了轨道角动量全息&#xff1b;实现了高维轨道角动量复用全息技术&#xff0c;获得了高安全的全息加密和超高容量全息信息系统。 1、轨道角动量自由度在全息中的引入 如图1所示&#xff0c;当全息图中没有携带轨道角动量的…

机器人学的AGI实现路径:从专用智能到通用认知的跨越

文章目录 引言:机器人学的范式革命一、AGI与机器人学的融合现状1.1 传统机器人系统的局限1.2 AGI技术为机器人学带来的变革1.3 关键里程碑案例二、AGI机器人的核心技术栈2.1 多模态感知融合2.2 认知架构设计2.3 具身认知实现路径三、AGI机器人的实现路径3.1 阶段式发展路线3.2…

香港中文大学 Adobe 推出 MotionCanvas:开启用户掌控的电影级图像视频创意之旅。

简介&#xff1a; 亮点直击 将电影镜头设计引入图像到视频的合成过程中。 推出了MotionCanvas&#xff0c;这是一种简化的视频合成系统&#xff0c;用于电影镜头设计&#xff0c;提供整体运动控制&#xff0c;以场景感知的方式联合操控相机和对象的运动。 设计了专门的运动条…

基于STM32的学习环境控制系统设计

&#x1f91e;&#x1f91e;大家好&#xff0c;这里是5132单片机毕设设计项目分享&#xff0c;今天给大家分享的是学习环境控制。 设备的详细功能见网盘中的文章《21、基于STM32的学习环境控制系统设计》&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1uWSZX2zbZwy9sY…

snort3.0-ubuntu18.04 64入侵检测安装与使用ailx10ailx10​​知乎知识会员

在日常生活中&#xff0c;很多人怀疑自己的手机、电脑被监控了&#xff0c;担心自己的隐私泄漏&#xff0c;实际上最佳的检测方式就是终端检测&#xff0c;也就是EDR&#xff0c;但是就是有那么多的人在网上大放厥词&#xff0c;说任何EDR杀毒软件都检测不到监控&#xff0c;毕…

使用亚马逊针对 PyTorch 和 MinIO 的 S3 连接器进行模型检查点处理

2023 年 11 月&#xff0c;Amazon 宣布推出适用于 PyTorch 的 S3 连接器。适用于 PyTorch 的 Amazon S3 连接器提供了专为 S3 对象存储构建的 PyTorch 数据集基元&#xff08;数据集和数据加载器&#xff09;的实现。它支持用于随机数据访问模式的地图样式数据集和用于流式处理…