多线程、多进程同时操作MMAP,会怎么样?(一)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 多线程、多进程同时操作MMAP,会怎么样?

接着上篇聊


此处为语雀内容卡片,点击链接查看:

www.yuque.com/ibaozi/dhzi…

回归正经


打游戏浪费生命,我们步入正轨,开始进一步改造进程间通信组件,上次我聊到三个痛点总结为两个维度:

  • 安全性
  • 同步性

安全性,我们通过对mmap映射文件路径的规范,挪至data/data目录下,保证不被其他应用访问到,后续可以通过pb+加密手段来防止root手机随意篡改映射文件内容。

今天我们先来聊聊同步,假设一种场景,就是在客户端Activity中,开启多个线程同时写入mmap内存中,看看会怎样

多线程写入


private fun testMultiThreadWriteMMIPC() {
    "set data".print(getProcessName())
    val countDownLatch = CountDownLatch(2)
    Thread {
        var index = 10000
        //重复写入一万次
        repeat(10000) {
            index++
            MMIPC.setData(index.toString(), index.toString())
        }
        countDownLatch.countDown()
    }.start()
    Thread {
        var index = 20000
        //重复写入一万次
        repeat(10000) {
            index++
            MMIPC.setData(index.toString(), index.toString())
        }
        countDownLatch.countDown()
    }.start()
    //等待两个线程结束
    countDownLatch.await()
    //打印写入数据的长度
    MMIPC.getData("").length.toString().print(getProcessName())
}

输出日志

2022-07-20 22:20:46.743 9933-9933/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: init
2022-07-20 22:20:46.744 9933-9933/com.zzy.mmipc D/MMIPC->: open file /data/user/0/com.zzy.mmipc/files
2022-07-20 22:20:46.744 9933-9933/com.zzy.mmipc E/MMIPC->: pid[9933]
2022-07-20 22:20:46.744 9933-9933/com.zzy.mmipc D/MMIPC->: open m_fd[78], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-20 22:20:46.744 9933-9933/com.zzy.mmipc D/MMIPC->: m_fd size[0]
2022-07-20 22:20:46.744 9933-9933/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 0, default_mmap_size 4161536
2022-07-20 22:20:46.744 9933-9933/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-20 22:20:46.845 9933-9933/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: set data
2022-07-20 22:20:46.913 9933-9933/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: 237004

正常长度等于20000*12 = 240000,因为setData,我会将key和value进行预处理变成如下,所以一次setData的长度是12,那么重试两万次就是 24万

string content = key + ":" + value + ",";

有什么办法可以解决该问题呢,往下看

HandlerThread


利用Looper机制,实现单线程中让所有的任务按顺序执行,直接代码验证效果,创建一个dev-looper分支,然后代码改变如下

image.png

然后测试结果如下,这次是240000

2022-07-20 22:43:47.874 11336-11336/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: init
2022-07-20 22:43:47.897 11336-11336/com.zzy.mmipc D/MMIPC->: open file /data/user/0/com.zzy.mmipc/files
2022-07-20 22:43:47.897 11336-11336/com.zzy.mmipc E/MMIPC->: pid[11336]
2022-07-20 22:43:47.897 11336-11336/com.zzy.mmipc D/MMIPC->: open m_fd[76], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-20 22:43:47.897 11336-11336/com.zzy.mmipc D/MMIPC->: m_fd size[0]
2022-07-20 22:43:47.897 11336-11336/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 0, default_mmap_size 4161536
2022-07-20 22:43:47.897 11336-11336/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-20 22:43:48.265 11336-11336/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: set data
2022-07-20 22:43:56.241 11336-11336/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: 240000

我们想想,这样实现有什么问题,功能没什么问题,但失去了原本用C++实现的好处,那就是跨平台能力,如果再IOS端,我们如何实现呢?接下来我们考虑用C++的方式实现对多线程写入

互斥锁(mutex)


该锁限制同一时间只有一个线程访问数据,实现如下,下面跨进程的时候在详细了解mutex

//声明
pthread_mutex_t m_lock;
//setData函数中使用
void MMIPC::setData(const string &key, const string &value) {
    //加锁
    pthread_mutex_lock(&m_lock);
    string content = key + ":" + value + ",";
//    ALOGD("setData content=%s", content.c_str());
    size_t numberOfBytes = content.length();
    if (m_position + numberOfBytes > m_file_size) {
        auto msg = "m_position: " + to_string(m_position) + ", numberOfBytes: " +
                   to_string(numberOfBytes) +
                   ", m_file_size: " + to_string(m_file_size);
        throw out_of_range(msg);
    }
    m_position = strlen(m_ptr);
    memcpy(m_ptr + m_position, (void *) content.c_str(), numberOfBytes);
//    ALOGD("setData success m_ptr.len=%d", m_position + numberOfBytes);
    //释放锁
    pthread_mutex_unlock(&m_lock);
}

测试日志

2022-07-20 23:04:10.958 12322-12322/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: init
2022-07-20 23:04:10.959 12322-12322/com.zzy.mmipc D/MMIPC->: open file /data/user/0/com.zzy.mmipc/files
2022-07-20 23:04:10.959 12322-12322/com.zzy.mmipc E/MMIPC->: pid[12322]
2022-07-20 23:04:10.959 12322-12322/com.zzy.mmipc D/MMIPC->: open m_fd[78], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-20 23:04:10.959 12322-12322/com.zzy.mmipc D/MMIPC->: m_fd size[0]
2022-07-20 23:04:10.959 12322-12322/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 0, default_mmap_size 4161536
2022-07-20 23:04:10.959 12322-12322/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-20 23:04:11.047 12322-12322/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: set data
2022-07-20 23:04:11.187 12322-12322/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: 240000

同样得到正确的结果,结果一样,哪种方式更好呢?这个给你留个作业,自己去验证下两种实现方式的耗时,用耗时来判断哪种更好,我初步判断是差不多的,因为Looper循环起来也是一个个处理的,而这个锁也一样,处理完一个,就处理下一个,但上锁和释放锁是有一定耗时的,估计在量上去后Looper更优秀。所以后面我们验证完了以后,可以用C++实现一个Looper,这样既可以做到跨平台,也可以做到不用锁。这期我们先跳过,优先实现多进程的读写,那么多进程有哪些方式实现同步呢?

多进程写


通过资料的查询,目前找到几种方案,分别是Semaphores和Mutex以及文件锁,下面就一一实践。

Semaphores


信号量提供了一种有效的进程间通信形式。协作进程可以使用信号量来同步对资源的访问,最常见的是共享内存。信号量还可以保护以下可供多个进程使用的资源免受不受控制的访问

  • 全局变量,例如文件变量、指针、计数器和数据结构。保护这些变量,防止多个进程同时访问。
  • 硬件资源,例如磁盘和磁带驱动器。硬件资源需要受控访问,因为同时访问会导致数据损坏。

看过介绍,发现它可以保护资源被多个进程同时访问,所以先来对它有个详细的了解,然后再用代码实战一下,看看效果。

概述


信号量用于控制进程对共享资源的访问。计数信号量有一个正整数值,表示可以同时锁定信号量的进程数。

分类

  • 命名信号量,命名信号量提供对多个进程之间资源的访问。
  • 未命名信号量,未命名的信号量提供对单个进程内或相关进程之间的资源的多种访问。

一些信号量函数专门设计用于对命名或未命名信号量执行操作。

如何操作

  • 信号量的值为正数,则锁定
  • 信号量值递减,进程继续执行,如果信号量的值为零或负数,则请求锁的进程将等待(被阻塞),直到另一个进程解锁该资源。

可能会阻塞多个进程以等待资源变得可用。

另外

信号量是全局实体,不与任何特定进程相关联。从这个意义上说,信号量没有所有者,因此无法出于任何目的(例如,错误恢复)跟踪信号量的所有权。

仅当使用共享资源的所有进程通过在不可用时等待信号量并在放弃资源时增加信号量值来合作时,信号量保护才起作用。由于信号量缺乏所有者,因此无法确定其中一个合作进程是否已变得不合作。使用信号量的应用程序必须仔细详细说明协作任务。共享资源的所有进程必须就哪个信号量控制资源达成一致。

接口

功能 描述
sem_close 释放指定的命名信号量
sem_destroy 销毁一个未命名的信号量
sem_getvalue 获取指定信号量的值
sem_init 初始化一个未命名的信号量
sem_open 打开/创建一个命名信号量供进程使用
sem_post 解锁锁定的信号量
sem_trywait 仅当信号量可以锁定信号量而无需等待另一个进程解锁它时才对信号量执行信号量锁定
sem_unlink 删除指定的命名信号量
sem_wait 对信号量执行信号量锁定

实战


通过上面的了解,我们先来改造下项目,实现一个跨进程写入的逻辑,如下:

image.png

子进程Activity改造

image.png

这样改造后,就可以实现多进程同时写操作,我们运行看下结果

2022-07-20 23:59:40.605 16531-16531/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: init
2022-07-20 23:59:40.606 16531-16531/com.zzy.mmipc D/MMIPC->: open file /data/user/0/com.zzy.mmipc/files
2022-07-20 23:59:40.606 16531-16531/com.zzy.mmipc E/MMIPC->: pid[16531]
2022-07-20 23:59:40.606 16531-16531/com.zzy.mmipc D/MMIPC->: open m_fd[78], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-20 23:59:40.606 16531-16531/com.zzy.mmipc D/MMIPC->: m_fd size[0]
2022-07-20 23:59:40.606 16531-16531/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 0, default_mmap_size 4161536
2022-07-20 23:59:40.606 16531-16531/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-20 23:59:40.704 16531-16531/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: set data
2022-07-20 23:59:40.860 16531-16531/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: 240000
2022-07-20 23:59:40.896 16564-16564/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: init
2022-07-20 23:59:40.897 16564-16564/com.zzy.mmipc D/MMIPC->: open file /data/user/0/com.zzy.mmipc/files
2022-07-20 23:59:40.897 16564-16564/com.zzy.mmipc E/MMIPC->: pid[16564]
2022-07-20 23:59:40.897 16564-16564/com.zzy.mmipc D/MMIPC->: open m_fd[78], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-20 23:59:40.897 16564-16564/com.zzy.mmipc D/MMIPC->: m_fd size[4161536]
2022-07-20 23:59:40.897 16564-16564/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 4161536, default_mmap_size 4161536
2022-07-20 23:59:40.897 16564-16564/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-20 23:59:46.110 16564-16564/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: 360000

发现由于MainActivity,写入太快了,导致后面子进程,初始化完成前已经写入完整,那我们加大MainActivity的写入次数。

2022-07-21 00:06:43.077 17809-17809/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: init
2022-07-21 00:06:43.080 17809-17809/com.zzy.mmipc D/MMIPC->: open file /data/user/0/com.zzy.mmipc/files
2022-07-21 00:06:43.080 17809-17809/com.zzy.mmipc E/MMIPC->: pid[17809]
2022-07-21 00:06:43.080 17809-17809/com.zzy.mmipc D/MMIPC->: open m_fd[76], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-21 00:06:43.080 17809-17809/com.zzy.mmipc D/MMIPC->: m_fd size[0]
2022-07-21 00:06:43.080 17809-17809/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 0, default_mmap_size 4161536
2022-07-21 00:06:43.081 17809-17809/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-21 00:06:43.472 17809-17809/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: set data
2022-07-21 00:06:43.600 17860-17860/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: init
2022-07-21 00:06:43.602 17860-17860/com.zzy.mmipc D/MMIPC->: open file /data/user/0/com.zzy.mmipc/files
2022-07-21 00:06:43.602 17860-17860/com.zzy.mmipc E/MMIPC->: pid[17860]
2022-07-21 00:06:43.602 17860-17860/com.zzy.mmipc D/MMIPC->: open m_fd[76], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-21 00:06:43.602 17860-17860/com.zzy.mmipc D/MMIPC->: m_fd size[4161536]
2022-07-21 00:06:43.602 17860-17860/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 4161536, default_mmap_size 4161536
2022-07-21 00:06:43.603 17860-17860/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-21 00:07:05.648 17860-17860/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: 1744548
2022-07-21 00:07:07.888 17809-17809/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: 1798260

可能是我手机性能太好,加了线程同步后,发现多进程写的时候并没有乱,于是我加大了重试次数,终于不负有心人,终于出错了,目前是重试15万次,正常应该是15*12 = 180万长度才对,打印的最终结果是1798260,差了1740,接下来我们想办法找回来

先用Semaphores试试,看能否找回1740,抱歉没实现成功,我们来用mutex试下

跨进程同步Mutex


开发中,Mutex能够保证多个线程对同一共享资源的互斥访问。互斥体(互斥对象)是一个程序对象,它被创建以便多个程序线程可以轮流共享同一资源,通常,当程序启动时,它会在开始时通过向系统请求给定资源来为给定资源创建互斥体,并且系统会为其返回唯一的名称或 ID。之后,任何需要该资源的线程都必须在使用该资源时使用互斥锁从其他线程锁定该资源。如果互斥锁已被锁定,则需要资源的线程通常由系统排队,然后在互斥锁解锁时获得控制权(再次,互斥锁在新线程使用资源期间被锁定)。

如果这个互斥对象我们使用共享内存创建,那就可以实现跨进程锁。那么如何创建呢?

struct shm_mutex {
    pthread_mutex_t mutex;
    pthread_mutexattr_t mutexattr;
};
inline void ShmMutex::createShmMutex(string dir) {
    int fd = open(dir.c_str(), O_CREAT | O_RDWR, 0666);
    if (fd == -1) {
        ALOGD("open fail for mutex: fd[%d], %s", fd, dir.c_str());
        return;
    }
    ALOGD("open success for mutex: fd[%d], %s", fd, dir.c_str());
    ftruncate(fd, sizeof(struct shm_mutex));
    shmMutex = static_cast<shm_mutex *>(mmap(NULL, sizeof(struct shm_mutex),
                                             PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
    if (shmMutex == MAP_FAILED) {
        ALOGD("mutex mmap failed");
        return;
    }
    if (close(fd) == 0) {
        fd = -1;
    } else {
        //fail to close
        ALOGD("mutex fd close failed");
    }
    memset(shmMutex, 0, sizeof(struct shm_mutex));
    pthread_mutexattr_init(&shmMutex->mutexattr);
    pthread_mutexattr_setpshared(&shmMutex->mutexattr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&shmMutex->mutex, &shmMutex->mutexattr);
    pthread_mutexattr_destroy(&shmMutex->mutexattr);
}

我们通过mmap创建一个共享内存,然后通过 pthread_mutexattr_init 初始化共享内存中的mutexattr,再设置PTHREAD_PROCESS_SHARED 跨进程互斥锁,最终 pthread_mutex_init 初始化 共享互斥对象 mutex。这样多个进程就可以通过一个mutex对象来实现互斥。上面的逻辑只是初始化,怎么使用呢?

第一 
ShmMutex mLock;
第二 在原来加锁的地方,加上ShmMutex锁
void MMIPC::setData(const string &key, const string &value) {
    AutoMutex autoMutex(mLock);
    string content = key + ":" + value + ",";
//    ALOGD("setData content=%s", content.c_str());
    size_t numberOfBytes = content.length();
    if (m_position + numberOfBytes > m_file_size) {
        auto msg = "m_position: " + to_string(m_position) + ", numberOfBytes: " +
                   to_string(numberOfBytes) +
                   ", m_file_size: " + to_string(m_file_size);
        throw out_of_range(msg);
    }
    m_position = strlen(m_ptr);
    memcpy(m_ptr + m_position, (void *) content.c_str(), numberOfBytes);
//    ALOGD("setData success m_ptr.len=%d", m_position + numberOfBytes);
}

你是不是发现了 AutoMutex,这里用到一个黑科技,想想我们之前加锁是这样

pthread_mutex_lock(&count_mutex);
pthread_mutex_unlock(&count_mutex);

lock、unlock 必须成对出现的,为什么呢?因为如果你加了锁之后,不释放,就会导致该资源无法被别人使用,从而产生死锁反应,那autoMutex怎么实现的自动释放呢?来为你揭晓答案

class ShmMutex {
public:
    enum {
        PRIVATE = 0,
        SHARED = 1
    };
    ShmMutex();
    ~ShmMutex();
    // lock or unlock the mutex
    status_t lock();
    void unlock();
    // lock if possible; returns 0 on success, error otherwise
    status_t tryLock();
    /**
     * 该方法,创建共享内存的Mutex,实现跨进程互斥锁
     * @param dir
     */
    void createShmMutex(string dir);
    // Manages the mutex automatically. It'll be locked when Autolock is
    // constructed and released when Autolock goes out of scope.
    class Autolock {
    public:
        inline Autolock(ShmMutex &mutex) : mLock(mutex) { mLock.lock(); }
        inline Autolock(ShmMutex *mutex) : mLock(*mutex) { mLock.lock(); }
        inline ~Autolock() { mLock.unlock(); }
    private:
        ShmMutex &mLock;
    };
private:
    ShmMutex(const ShmMutex &);
    ShmMutex &operator=(const ShmMutex &);
    struct shm_mutex *shmMutex;
};

你看~Autolock()析构函数就明白了,它利用了C++的构造和析构函数特性,也就是说析构函数在对象被销毁时自动调用,而我们在setData中,autoMutex使用的栈内存,所以当函数出栈时,会自动释放栈内的对象。这样就完美实现了成对的锁。牛吹了半天,下面是验证的时候了,跑下封装好的代码,看看是否是正常输出180万的长度,请看

2022-07-23 18:44:31.241 12534-12534/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: init
2022-07-23 18:44:31.242 12534-12534/com.zzy.mmipc D/MMIPC->: root dir /data/user/0/com.zzy.mmipc/files
2022-07-23 18:44:31.242 12534-12534/com.zzy.mmipc D/MMIPC->: open success for mutex: fd[78], /data/user/0/com.zzy.mmipc/files/default_mutex.ipc
2022-07-23 18:44:31.242 12534-12534/com.zzy.mmipc E/MMIPC->: pid[12534]
2022-07-23 18:44:31.242 12534-12534/com.zzy.mmipc D/MMIPC->: open m_fd[78], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-23 18:44:31.242 12534-12534/com.zzy.mmipc D/MMIPC->: m_fd size[0]
2022-07-23 18:44:31.242 12534-12534/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 0, default_mmap_size 4161536
2022-07-23 18:44:31.242 12534-12534/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-23 18:44:31.369 12534-12534/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: set data
2022-07-23 18:44:31.472 12575-12575/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: init
2022-07-23 18:44:31.473 12575-12575/com.zzy.mmipc D/MMIPC->: root dir /data/user/0/com.zzy.mmipc/files
2022-07-23 18:44:31.473 12575-12575/com.zzy.mmipc D/MMIPC->: open success for mutex: fd[76], /data/user/0/com.zzy.mmipc/files/default_mutex.ipc
2022-07-23 18:44:31.473 12575-12575/com.zzy.mmipc E/MMIPC->: pid[12575]
2022-07-23 18:44:31.473 12575-12575/com.zzy.mmipc D/MMIPC->: open m_fd[76], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc
2022-07-23 18:44:31.473 12575-12575/com.zzy.mmipc D/MMIPC->: m_fd size[4161536]
2022-07-23 18:44:31.473 12575-12575/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 4161536, default_mmap_size 4161536
2022-07-23 18:44:31.473 12575-12575/com.zzy.mmipc D/MMIPC->: mmap success
2022-07-23 18:44:34.273 12534-12534/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: 1192464
2022-07-23 18:44:42.746 12575-12575/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: 1800000

看到了没,1800000,完美实现跨进程同步。但这样就结束了吗?由于我们使用了跨进程的互斥锁,如果该进程在上锁后,并且未释放之前,进程崩溃了怎么办?咋整?我们能不能做到在进程崩溃前,自动释放呢?通过搜寻,我找到了答案,我们可以使用

pthread_mutexattr_setrobust() ,将 pthread 互斥锁初始化为“robust”,如果持有互斥锁的进程死了,下一个获取它的线程将收到 EOWNERDEAD(但仍然成功获取互斥锁),以便它知道执行任何清理。然后它需要使用 pthread_mutex_consistent() 通知获取的互斥锁再次一致。但一个不好的消息,如图:

image.png

那就是android 的ndk,没有这个函数,而且我看了google 官方项目Issue对这个做了回答: github.com/android/ndk…,意思是目前还没支持。怎么办呢?现在只剩下最后一条路,那就是文件锁了。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
24天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
39 1
|
3天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
8天前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
5天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
14 1
|
10天前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
23 2
|
12天前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
1月前
|
存储 消息中间件 人工智能
进程,线程,协程 - 你了解多少?
本故事采用简洁明了的对话方式,尽洪荒之力让你在轻松无负担的氛围中,稍微深入地理解进程、线程和协程的相关原理知识
40 2
进程,线程,协程 - 你了解多少?
|
22天前
|
Python
Python中的多线程与多进程
本文将探讨Python中多线程和多进程的基本概念、使用场景以及实现方式。通过对比分析,我们将了解何时使用多线程或多进程更为合适,并提供一些实用的代码示例来帮助读者更好地理解这两种并发编程技术。
|
25天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第15天】进程、线程和协程是操作系统中三种不同的执行单元。进程是资源分配和调度的基本单位,每个进程有独立的内存空间;线程是进程内的执行路径,共享进程资源,切换成本较低;协程则更轻量,由用户态调度,适合处理高并发和IO密集型任务。进程提供高隔离性和安全性,线程支持高并发,协程则在资源消耗和调度灵活性方面表现优异。
44 2
|
1月前
|
算法 安全 调度
深入理解操作系统:进程与线程的管理
【10月更文挑战第9天】在数字世界的心脏跳动着的,不是别的,正是操作系统。它如同一位无形的指挥家,协调着硬件与软件的和谐合作。本文将揭开操作系统中进程与线程管理的神秘面纱,通过浅显易懂的语言和生动的比喻,带你走进这一复杂而又精妙的世界。我们将从进程的诞生讲起,探索线程的微妙关系,直至深入内核,理解调度算法的智慧。让我们一起跟随代码的脚步,解锁操作系统的更多秘密。
35 1

相关实验场景

更多