精华内容
下载资源
问答
  • 线程间通信

    2014-03-31 11:52:49
    定义线程间通信,其中包含里。java线程安全。
  • 线程间通信的几种实现方式

    万次阅读 多人点赞 2019-01-13 07:43:41
    线程间通信的几种实现方式 首先,要短信线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析: 题目:有两个线程A、B,A线程向一个集合...

    线程间通信的几种实现方式

    首先,要短信线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析:
    

    题目:有两个线程A、B,A线程向一个集合里面依次添加元素"abc"字符串,一共添加十次,当添加到第五次的时候,希望B线程能够收到A线程的通知,然后B线程执行相关的业务操作


    方式一:使用 volatile 关键字

    基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想,大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式

    public class TestSync {
        // 定义一个共享变量来实现通信,它需要是volatile修饰,否则线程不能及时感知
        static volatile boolean notice = false;
    
        public static void main(String[] args) {
            List<String>  list = new ArrayList<>();
            // 实现线程A
            Thread threadA = new Thread(() -> {
                for (int i = 1; i <= 10; i++) {
                    list.add("abc");
                    System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (list.size() == 5)
                        notice = true;
                }
            });
            // 实现线程B
            Thread threadB = new Thread(() -> {
                while (true) {
                    if (notice) {
                        System.out.println("线程B收到通知,开始执行自己的业务...");
                        break;
                    }
                }
            });
            // 需要先启动线程B
            threadB.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 再启动线程A
            threadA.start();
        }
    }
    

    运行结果为:
    在这里插入图片描述

    方式二:使用Object类的wait() 和 notify() 方法

    众所周知,Object类提供了线程间通信的方法:wait()notify()notifyaAl(),它们是多线程通信的基础,而这种实现方式的思想自然是线程间通信。

    注意: wait和 notify必须配合synchronized使用,wait方法释放锁,notify方法不释放锁

    public class TestSync {
        public static void main(String[] args) {
            // 定义一个锁对象
            Object lock = new Object();
            List<String>  list = new ArrayList<>();
            // 实现线程A
            Thread threadA = new Thread(() -> {
                synchronized (lock) {
                    for (int i = 1; i <= 10; i++) {
                        list.add("abc");
                        System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if (list.size() == 5)
                            lock.notify();// 唤醒B线程
                    }
                }
            });
            // 实现线程B
            Thread threadB = new Thread(() -> {
                while (true) {
                    synchronized (lock) {
                        if (list.size() != 5) {
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("线程B收到通知,开始执行自己的业务...");
                    }
                }
            });
            // 需要先启动线程B
            threadB.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 再启动线程A
            threadA.start();
        }
    }
    

    运行结果为
    在这里插入图片描述由打印结果截图可知,在线程A发出notify()唤醒通知之后,依然是走完了自己线程的业务之后,线程B才开始执行,这也正好说明了,notify()方法不释放锁,而wait()方法释放锁。

    方式三:使用JUC工具类 CountDownLatch

    jdk1.5之后在java.util.concurrent包下提供了很多并发编程相关的工具类,简化了我们的并发编程代码的书写,***CountDownLatch***基于AQS框架,相当于也是维护了一个线程间共享变量state

    public class TestSync {
        public static void main(String[] args) {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            List<String>  list = new ArrayList<>();
            // 实现线程A
            Thread threadA = new Thread(() -> {
                for (int i = 1; i <= 10; i++) {
                    list.add("abc");
                    System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (list.size() == 5)
                        countDownLatch.countDown();
                }
            });
            // 实现线程B
            Thread threadB = new Thread(() -> {
                while (true) {
                    if (list.size() != 5) {
                        try {
                            countDownLatch.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                    break;
                }
            });
            // 需要先启动线程B
            threadB.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 再启动线程A
            threadA.start();
        }
    }
    

    运行结果为:
    在这里插入图片描述

    使用 ReentrantLock 结合 Condition

    public class TestSync {
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
    
            List<String> list = new ArrayList<>();
            // 实现线程A
            Thread threadA = new Thread(() -> {
                lock.lock();
                for (int i = 1; i <= 10; i++) {
                    list.add("abc");
                    System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (list.size() == 5)
                        condition.signal();
    
                }
                lock.unlock();
            });
            // 实现线程B
            Thread threadB = new Thread(() -> {
                lock.lock();
                if (list.size() != 5) {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程B收到通知,开始执行自己的业务...");
                lock.unlock();
            });
            threadB.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadA.start();
        }
    }
    

    运行结果为:
    在这里插入图片描述
    显然这种方式使用起来并不是很好,代码编写复杂,而且线程B在被A唤醒之后由于没有获取锁还是不能立即执行,也就是说,A在唤醒操作之后,并不释放锁。这种方法跟 Objectwait()notify() 一样。

    方式五:基本LockSupport实现线程间的阻塞和唤醒

    LockSupport 是一种非常灵活的实现线程间阻塞和唤醒的工具,使用它不用关注是等待线程先进行还是唤醒线程先运行,但是得知道线程的名字。

    public class TestSync {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            // 实现线程B
            final Thread threadB = new Thread(() -> {
                if (list.size() != 5) {
                    LockSupport.park();
                }
                System.out.println("线程B收到通知,开始执行自己的业务...");
            });
            // 实现线程A
            Thread threadA = new Thread(() -> {
                for (int i = 1; i <= 10; i++) {
                    list.add("abc");
                    System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (list.size() == 5)
                        LockSupport.unpark(threadB);
                }
            });
            threadA.start();
            threadB.start();
        }
    }
    

    运行结果
    在这里插入图片描述

    展开全文
  • MFC线程间通信

    2017-04-05 19:23:45
    线程间的通信一般采用四种方式:全局变量方式、消息传递方式、参数传递方式和线程同步法。线程间通信最简单的一种方法是使用全局变量,这用的就是全局变量的方式。
  • 主要介绍了Python如何实现线程间通信,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
  • 主要介绍了Java线程间通信不同步问题,结合实例形式分析了java线程间通信不同步问题的原理并模拟实现了线程间通信不同步情况下的异常输出,需要的朋友可以参考下
  • 进程间通信和线程间通信

    千次阅读 2020-08-18 20:26:22
    线程间通信 进程和线程的区别 程序只是一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。而进程则不同,它是程序在某个数据集上的执行。进程是一个动态的实体,它有自己的生命周期。它因...

    进程间通信 转自  https://www.cnblogs.com/LUO77/p/5816326.html

    线程间通信  https://www.cnblogs.com/jobs1/p/10784021.html

    线程间通信 

     

    进程和线程的区别

    程序只是一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。而进程则不同,它是程序在某个数据集上的执行。进程是一个动态的实体,它有自己的生命周期。它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消。反映了一个程序在一定的数据集上运行的全部动态过程。

    进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

    线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。

    一个程序至少一个进程,一个进程至少一个线程。

    为什么会有线程?

      每个进程都有自己的地址空间,即进程空间,在网络或多用户换机下,一个服务器通常需要接收大量不确定数量用户的并发请求,为每一个请求都创建一个进程显然行不通(系统开销大响应用户请求效率低),因此操作系统中线程概念被引进。

    • 线程的执行过程是线性的,尽管中间会发生中断或者暂停,但是进程所拥有的资源只为改线状执行过程服务,一旦发生线程切换,这些资源需要被保护起来。
    • 进程分为单线程进程和多线程进程,单线程进程宏观来看也是线性执行过程,微观上只有单一的执行过程。多线程进程宏观是线性的,微观上多个执行操作。

    线程的改变只代表CPU的执行过程的改变,而没有发生进程所拥有的资源的变化。 

    进程线程的区别:

    • 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
    • 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。

         一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

         进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程

    • 执行过程:每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
    • 线程是处理器调度的基本单位,但是进程不是。
    • 两者均可并发执行。

    优缺点:

      线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。

      进程执行开销大,但是能够很好的进行资源管理和保护。进程可以跨机器前移。

    何时使用多进程,何时使用多线程?

    对资源的管理和保护要求高,不限制开销和效率时,使用多进程。

    要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。

     

    进程间通信

    多进程:

    首先,先来讲一下fork之后,发生了什么事情。

    由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程 id。将子进程id返回给父进程的理由是:因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以获得其所有子进程的进程id。对子进程来说,之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。(进程id 0总是由交换进程使用,所以一个子进程的进程id不可能为0 )。

    fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置两进程的程序计数器pc值相同,也就是说,子进程是从fork返回处开始执行的),但有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。
    可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。这也是fork为什么叫fork的原因

    至于那一个最先运行,可能与操作系统(调度算法)有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同,可以通过原语的办法解决。


     

    常见的通信方式:

    1. 管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
    2. 命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
    4. 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
    5. 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
    6. 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    7. 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
    8. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

     

    信号:

    信号是Linux系统中用于进程之间通信或操作的一种机制,信号可以在任何时候发送给某一进程,而无须知道该进程的状态。如果该进程并未处于执行状态,则该信号就由内核保存起来,知道该进程恢复执行并传递给他为止。如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。

     

    Linux提供了几十种信号,分别代表着不同的意义。信号之间依靠他们的值来区分,但是通常在程序中使用信号的名字来表示一个信号。在Linux系统中,这些信号和以他们的名称命名的常量被定义在/usr/includebitssignum.h文件中。通常程序中直接包含<signal.h>就好。

     

    信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式,信号可以在用户空间进程和内核之间直接交互。内核也可以利用信号来通知用户空间的进程来通知用户空间发生了哪些系统事件。信号事件有两个来源:

    1)硬件来源,例如按下了cltr+C,通常产生中断信号sigint

    2)软件来源,例如使用系统调用或者命令发出信号。最常用的发送信号的系统函数是kill,raise,setitimer,sigation,sigqueue函数。软件来源还包括一些非法运算等操作。

     

    一旦有信号产生,用户进程对信号产生的相应有三种方式:

    1)执行默认操作,linux对每种信号都规定了默认操作。

    2)捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数。

    3)忽略信号,当不希望接收到的信号对进程的执行产生影响,而让进程继续执行时,可以忽略该信号,即不对信号进程作任何处理。

      有两个信号是应用进程无法捕捉和忽略的,即SIGKILL和SEGSTOP,这是为了使系统管理员能在任何时候中断或结束某一特定的进程。

    上图表示了Linux中常见的命令

    1、信号发送:

    信号发送的关键使得系统知道向哪个进程发送信号以及发送什么信号。下面是信号操作中常用的函数:

    例子:创建子进程,为了使子进程不在父进程发出信号前结束,子进程中使用raise函数发送sigstop信号,使自己暂停;父进程使用信号操作的kill函数,向子进程发送sigkill信号,子进程收到此信号,结束子进程。

    2、信号处理

    当某个信号被发送到一个正在运行的进程时,该进程即对次特定的信号注册相应的信号处理函数,以完成所需处理。设置信号处理方式的是signal函数,在程序正常结束前,在应用signal函数恢复系统对信号的

    默认处理方式。

    3.信号阻塞

    有时候既不希望进程在接收到信号时立刻中断进程的执行,也不希望此信号完全被忽略掉,而是希望延迟一段时间再去调用信号处理函数,这个时候就需要信号阻塞来完成。

     

    例子:主程序阻塞了cltr+c的sigint信号。用sigpromask将sigint假如阻塞信号集合。

     

    管道:

    管道允许在进程之间按先进先出的方式传送数据,是进程间通信的一种常见方式。

    管道是Linux 支持的最初Unix IPC形式之一,具有以下特点:

    1) 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

    2) 匿名管道只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);

    3) 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

     

    管道分为pipe(无名管道)和fifo(命名管道)两种,除了建立、打开、删除的方式不同外,这两种管道几乎是一样的。他们都是通过内核缓冲区实现数据传输。

    • pipe用于相关进程之间的通信,例如父进程和子进程,它通过pipe()系统调用来创建并打开,当最后一个使用它的进程关闭对他的引用时,pipe将自动撤销。
    • FIFO即命名管道,在磁盘上有对应的节点,但没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。

    管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或慢的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。

    无名管道:

    pipe的例子:父进程创建管道,并在管道中写入数据,而子进程从管道读出数据

    命名管道:

    和无名管道的主要区别在于,命名管道有一个名字,命名管道的名字对应于一个磁盘索引节点,有了这个文件名,任何进程有相应的权限都可以对它进行访问。

    而无名管道却不同,进程只能访问自己或祖先创建的管道,而不能访任意访问已经存在的管道——因为没有名字。

     

    Linux中通过系统调用mknod()或makefifo()来创建一个命名管道。最简单的方式是通过直接使用shell

    mkfifo myfifo

     

     等价于

    mknod myfifo p

     

    以上命令在当前目录下创建了一个名为myfifo的命名管道。用ls -p命令查看文件的类型时,可以看到命名管道对应的文件名后有一条竖线"|",表示该文件不是普通文件而是命名管道。

    使用open()函数通过文件名可以打开已经创建的命名管道,而无名管道不能由open来打开。当一个命名管道不再被任何进程打开时,它没有消失,还可以再次被打开,就像打开一个磁盘文件一样。

    可以用删除普通文件的方法将其删除,实际删除的事磁盘上对应的节点信息。

    例子:用命名管道实现聊天程序,一个张三端,一个李四端。两个程序都建立两个命名管道,fifo1,fifo2,张三写fifo1,李四读fifo1;李四写fifo2,张三读fifo2。

    用select把,管道描述符和stdin假如集合,用select进行阻塞,如果有i/o的时候唤醒进程。(粉红色部分为select部分,黄色部分为命名管道部分)

     

     

     

    在linux系统中,除了用pipe系统调用建立管道外,还可以使用C函数库中管道函数popen函数来建立管道,使用pclose关闭管道。

    例子:设计一个程序用popen创建管道,实现 ls -l |grep main.c的功能

    分析:先用popen函数创建一个读管道,调用fread函数将ls -l的结果存入buf变量,用printf函数输出内容,用pclose关闭读管道;

    接着用popen函数创建一个写管道,调用fprintf函数将buf的内容写入管道,运行grep命令。

    popen的函数原型:

    FILE* popen(const char* command,const char* type);

     

    参数说明:command是子进程要执行的命令,type表示管道的类型,r表示读管道,w代表写管道。如果成功返回管道文件的指针,否则返回NULL。

    使用popen函数读写管道,实际上也是调用pipe函数调用建立一个管道,再调用fork函数建立子进程,接着会建立一个shell 环境,并在这个shell环境中执行参数所指定的进程。

    消息队列:

    消息队列,就是一个消息的链表,是一系列保存在内核中消息的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。

    消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。

    可以把消息看做一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程可以从消息队列中读取消息。

    消息队列的常用函数如下表:

    进程间通过消息队列通信,主要是:创建或打开消息队列,添加消息,读取消息和控制消息队列。

    例子:用函数msget创建消息队列,调用msgsnd函数,把输入的字符串添加到消息队列中,然后调用msgrcv函数,读取消息队列中的消息并打印输出,最后再调用msgctl函数,删除系统内核中的消息队列。(黄色部分是消息队列相关的关键代码,粉色部分是读取stdin的关键代码)

    共享内存:

    共享内存允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取错做读出,从而实现了进程间的通信。

     

    采用共享内存进行通信的一个主要好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝,对于像管道和消息队里等通信方式,则需要再内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次:一次从输入文件到共享内存区,另一次从共享内存到输出文件。

    一般而言,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时在重新建立共享内存区域;而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件,因此,采用共享内存的通信方式效率非常高。

    共享内存有两种实现方式:1、内存映射 2、共享内存机制

    1、内存映射

    内存映射 memory map机制使进程之间通过映射同一个普通文件实现共享内存,通过mmap()系统调用实现。普通文件被映射到进程地址空间后,进程可以

    像访问普通内存一样对文件进行访问,不必再调用read/write等文件操作函数。

    例子:创建子进程,父子进程通过匿名映射实现共享内存。

    分析:主程序中先调用mmap映射内存,然后再调用fork函数创建进程。那么在调用fork函数之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap函数的返回地址,这样,父子进程就可以通过映射区域进行通信了。

    2、UNIX System V共享内存机制

    IPC的共享内存指的是把所有的共享数据放在共享内存区域(IPC shared memory region),任何想要访问该数据的进程都必须在本进程的地址空间新增一块内存区域,用来映射存放共享数据的物理内存页面。

    和前面的mmap系统调用通过映射一个普通文件实现共享内存不同,UNIX system V共享内存是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。

    例子:设计两个程序,通过unix system v共享内存机制,一个程序写入共享区域,另一个程序读取共享区域。

    分析:一个程序调用fotk函数产生标准的key,接着调用shmget函数,获取共享内存区域的id,调用shmat函数,映射内存,循环计算年龄,另一个程序读取共享内存。

    (fotk函数在消息队列部分已经用过了,

    根据pathname指定的文件(或目录)名称,以及proj参数指定的数字,ftok函数为IPC对象生成一个唯一性的键值。)

    key_t ftok(char* pathname,char proj)

     

    c++ 线程间通信方式

     

    一:两个进程间的两个线程通信,相当于进程间通信

    二:一个进程中的两个线程间通信

      通信方式:

    1.互斥锁

      mutex;

      lock_guard (在构造函数里加锁,在析构函数里解锁)

      unique_lock 自动加锁、解锁

     

    2.读写锁

      shared_lock

    3.信号量

      c++11中未实现,可以自己使用mutex和conditon_variable 实现

      代码实现如下: 

    #pragma once
    #include <mutex>
    #include <condition_variable>
    class Semaphore
    {
    public:
     explicit Semaphore(unsigned int count); //用无符号数表示信号量资源 
     ~Semaphore();

    public:
     void wait();
     void signal();
    private:
     int m_count; //计数器必须是有符号数 
     std::mutex m_mutex;
     std::condition_variable m_condition_variable;
    };

     

    #include "Semaphore.h"

    Semaphore::Semaphore(unsigned int count) :m_count(count) {
    }

    Semaphore::~Semaphore()
    {
    }

    void Semaphore::wait() {
     std::unique_lock<std::mutex> unique_lock(m_mutex);
     --m_count;
     while (m_count < 0) {
      m_condition_variable.wait(unique_lock);
     }
    }

    void Semaphore::signal() {
     std::lock_guard<std::mutex> lg(m_mutex);
     if (++m_count < 1) {
      m_condition_variable.notify_one();
     }
    }

    4.条件变量

      condition_variable

    展开全文
  • iOS线程间通信

    千次阅读 2019-01-08 15:20:06
    什么叫做线程间通信  在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信  线程间通信的体现  1个线程传递数据给另1个线程  在1个线程中执行完特定任务后,转到另1个线程继续执行任务  ...

    什么叫做线程间通信

    •  在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信

     线程间通信的体现

    •  1个线程传递数据给另1个线程
    •   在1个线程中执行完特定任务后,转到另1个线程继续执行任务

     线程间通信常用方法

    1. NSThread :一个线程传递数据给另一个线程

    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
    
    //具体用法如下:
    //点击屏幕开始执行下载方法
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        [self performSelectorInBackground:@selector(download) withObject:nil];
    }
    
    //下载图片
    - (void)download
    {    
        // 1.图片地址
        NSString *urlStr = @"http://d.jpg"; 
        NSURL *url = [NSURL URLWithString:urlStr]; 
        // 2.根据地址下载图片的二进制数据 
        NSData *data = [NSData dataWithContentsOfURL:url]; 
        // 3.设置图片
        UIImage *image = [UIImage imageWithData:data]; 
        // 4.回到主线程,刷新UI界面(为了线程安全)
        [self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO]; 
       // [self performSelector:@selector(downloadFinished:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES]; 
        
    }
    
    - (void)downloadFinished:(UIImage *)image
    {
        self.imageView.image = image; 
        NSLog(@"downloadFinished---%@", [NSThread currentThread]);
    }
     

    2. GCD :一个线程传递数据给另一个线程

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event    
    {  
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
            NSLog(@"donwload---%@", [NSThread currentThread]);
            
            // 1.子线程下载图片
            NSURL *url = [NSURL URLWithString:@"http://d.jpg"];
            
            NSData *data = [NSData dataWithContentsOfURL:url];
            
            UIImage *image = [UIImage imageWithData:data];
            
            // 2.回到主线程设置图片
            dispatch_async(dispatch_get_main_queue(), ^{
                
                NSLog(@"setting---%@ %@", [NSThread currentThread], image);
                
                [self.button setImage:image forState:UIControlStateNormal];
            });
        });
    }
     

    3. NSMachPort 

    NSPort有3个子类,NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。

    #define kMsg1 100
    #define kMsg2 101
     
    - (void)viewDidLoad {
        [super viewDidLoad]; 
        //1. 创建主线程的port
        // 子线程通过此端口发送消息给主线程
        NSPort *myPort = [NSMachPort port]; 
        //2. 设置port的代理回调对象
        myPort.delegate = self; 
        //3. 把port加入runloop,接收port消息
        [[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode]; 
        //4. 启动次线程,并传入主线程的port
        MyWorkerClass *work = [[MyWorkerClass alloc] init];
        [NSThread detachNewThreadSelector:@selector(launchThreadWithPort:)
                                 toTarget:work
                               withObject:myPort];
    }
    
    - (void)handlePortMessage:(NSMessagePort*)message{ 
        NSLog(@"接到子线程传递的消息!%@",message); 
        //1. 消息id
        NSUInteger msgId = [[message valueForKeyPath:@"msgid"] integerValue]; 
        //2. 当前主线程的port
        NSPort *localPort = [message valueForKeyPath:@"localPort"]; 
        //3. 接收到消息的port(来自其他线程)
        NSPort *remotePort = [message valueForKeyPath:@"remotePort"];
     
        if (msgId == kMsg1)
        {
            //向子线的port发送消息
            [remotePort sendBeforeDate:[NSDate date]
                                 msgid:kMsg2
                            components:nil
                                  from:localPort
                              reserved:0];
     
        }else if (msgId == kMsg2){
            NSLog(@"操作2....\n");
        }
    }
    
    //MyWorkerClass类
    
    #import "MyWorkerClass.h"
     
    @interface MyWorkerClass() <NSMachPortDelegate> {
        NSPort *remotePort;
        NSPort *myPort;
    }
    @end
     
    #define kMsg1 100
    #define kMsg2 101
     
    @implementation MyWorkerClass
     
    - (void)launchThreadWithPort:(NSPort *)port {
     
        @autoreleasepool {
            //1. 保存主线程传入的port
            remotePort = port;
            //2. 设置子线程名字
            [[NSThread currentThread] setName:@"MyWorkerClassThread"];
            //3. 开启runloop
            [[NSRunLoop currentRunLoop] run];
            //4. 创建自己port
            myPort = [NSPort port];
            //5.
            myPort.delegate = self;
            //6. 将自己的port添加到runloop
            //作用1、防止runloop执行完毕之后推出
            //作用2、接收主线程发送过来的port消息
            [[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];
            //7. 完成向主线程port发送消息
            [self sendPortMessage];
        }
    }
     
    /**
     *   完成向主线程发送port消息
     */
    - (void)sendPortMessage {
     
        NSMutableArray *array  =[[NSMutableArray alloc]initWithArray:@[@"1",@"2"]];
        //发送消息到主线程,操作1
        [remotePort sendBeforeDate:[NSDate date]
                             msgid:kMsg1
                        components:array
                              from:myPort
                          reserved:0]; 
    }
     
    #pragma mark - NSPortDelegate
    /**
     *  接收到主线程port消息
     */
    - (void)handlePortMessage:(NSPortMessage *)message
    {
        NSLog(@"接收到父线程的消息...\n"); 
    }
     
    @end
    

     

    展开全文
  • 主要介绍了Java的线程同步以及线程间通信,多线程编程是Java学习中的重点和难点,需要的朋友可以参考下
  • 下面小编就为大家带来一篇java多线程编程学习(线程间通信)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • android 线程间通信

    2012-05-28 13:42:39
    主要描述了android的线程间通信的原理和应用
  • Android线程间通信的Message机制Android线程间通信的Message机制Android线程间通信的Message机制Android线程间通信的Message机制Android线程间通信的Message机制
  • 主要介绍了java线程间通信的通俗解释,介绍了线程通信中的几个相关概念,然后分享了线程通信的实现方式及代码示例,具有一定参考价值 ,需要的朋友可以了解下。
  • 本篇文章是对Android的Handler与线程间通信ITC进行了详细的分析介绍,需要的朋友参考下
  • Java线程间通信

    千次阅读 2020-05-23 00:18:32
    什么是线程间通信 线程间通信其实就是多个线程在操作同一个资源,但是操作的动作不同 Demo前奏 两个线程操作同一对象,一个线程负责给对象切换赋值一次,另一个线程负责打印一次对象值 package com.star.test; ...

    什么是线程间通信

    线程间通信其实就是多个线程在操作同一个资源,但是操作的动作不同

    Demo前奏

    两个线程操作同一对象,一个线程负责给对象切换赋值一次,另一个线程负责打印一次对象值

    package com.star.test;
    
    class Person{
        //名字
        private String name;
        //性别
        private String sex;
    
        /**
         * 对象赋值
         * @param name
         * @param sex
         */
        public void setValue(String name, String sex) {
            this.name = name;
            this.sex = sex;
        }
    
        /**
         * 打印对象值
         */
        public void printValue() {
            System.out.println(name + "-----------" + sex);
        }
    }
    class InputValue implements Runnable {
        private Person person;
    
        InputValue(Person p) {
            this.person = p;
        }
    
        @Override
        public void run() {
            int i = 0;
            //循环给对象赋值
            while (true) {
                if (i == 0) {
                    person.setValue("喜羊羊","男");
                } else {
                    person.setValue("grey wolf","man");
                }
                i = (i + 1) % 2;
            }
        }
    }
    class OutputValue implements Runnable {
        private Person person;
    
        OutputValue(Person p) {
            this.person = p;
        }
    
        @Override
        public void run() {
            //循环打印对象值
            while (true) {
                person.printValue();
            }
        }
    }
    public class StarTest {
        public static void main(String[] args) {
            Person person = new Person();
            new Thread(new InputValue(person)).start();
            new Thread(new OutputValue(person)).start();
        }
    }
    

    输出结果如下:
    在这里插入图片描述
      可以发现,打印结果出现对不上的情况,也就是英文 name 对应中文 sex、中文 name 对应英文 sex,此时就是线程不安全引发的,我们将代码改造为线程安全的:

    改造代码

    /**
     * 对象赋值
     * @param name
     * @param sex
     */
    public void setValue(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }
    
    /**
     * 打印对象值
     */
    public void printValue() {
        System.out.println(name + "-----------" + sex);
    }
    

    改为

    /**
     * 对象赋值
     * @param name
     * @param sex
     */
    public synchronized void setValue(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }
    
    /**
     * 打印对象值
     */
    public synchronized void printValue() {
        System.out.println(name + "-----------" + sex);
    }
    

    运行结果:
    在这里插入图片描述
    此时我们发现:这种结果不是我们想要的,应该是赋值一次、打印一次,此时就用到了线程通信

    线程通信------等待唤醒机制

    package com.star.test;
    
    class Person{
        //名字
        private String name;
        //性别
        private String sex;
        //是否赋值标识
        boolean flag = false;
    
        /**
         * 对象赋值
         * @param name
         * @param sex
         */
        public synchronized void setValue(String name, String sex) {
            if (flag) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.name = name;
            this.sex = sex;
            //赋值改变标识
            flag = true;
            //唤醒等待线程
            notify();
        }
    
        /**
         * 打印对象值
         */
        public synchronized void printValue() {
            if (!flag) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(name + "-----------" + sex);
            //改变标识
            flag = false;
            //唤醒等待线程
            notify();
        }
    }
    class InputValue implements Runnable {
        private Person person;
    
        InputValue(Person p) {
            this.person = p;
        }
    
        @Override
        public void run() {
            int i = 0;
            //循环给对象赋值
            while (true) {
                if (i == 0) {
                    person.setValue("喜羊羊","男");
                } else {
                    person.setValue("grey wolf","man");
                }
                i = (i + 1) % 2;
            }
        }
    }
    class OutputValue implements Runnable {
        private Person person;
    
        OutputValue(Person p) {
            this.person = p;
        }
    
        @Override
        public void run() {
            //循环打印对象值
            while (true) {
                person.printValue();
            }
        }
    }
    public class StarTest {
        public static void main(String[] args) {
            Person person = new Person();
            new Thread(new InputValue(person)).start();
            new Thread(new OutputValue(person)).start();
        }
    }
    

    运行结果:
    在这里插入图片描述

    原理图:

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200521232231960.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTY2MTQ2Ng==,size_16,color_FFFFFF,t_70
    如图所示
      赋值线程运行时,判断 flag 为 false 时赋值,赋值后改变 flag 为 true,并且叫醒等待线程,flag 为 true 时线程状态转换为等待;
      打印线程运行时,判断 flag 为 false 时进入等待状态,为 true 时则打印,之后改变 flag 为 false,并叫醒等待线程
    注意: notify() 方法是随机叫醒等待中的一个线程,上面的例子中,等待中的线程只有一个

    生产消费者

    1、单个生产、消费者

    package com.star.test;
    
    class Resource {
        //资源名称
        private String name;
        //数量
        private int count = 0;
        //标识
        private boolean flag = false;
    
        //生产
        public synchronized void set(String name) {
            if (flag) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.name = name;
            System.out.println(Thread.currentThread().getName() + "---生产者生产---" + this.name + ",剩余数量:" + (++count));
            flag = true;
            notify();
        }
    
        //消费
        public synchronized void get() {
            if (!flag) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "******消费者消费******" + this.name + ",剩余数量:" + (--count));
            flag = false;
            notify();
        }
    }
    
    /**
     * 生产者
     */
    class Producer implements Runnable {
        private Resource resource;
    
        Producer(Resource res) {
            this.resource = res;
        }
    
        @Override
        public void run() {
            while (true) {
                resource.set("衣服");
            }
        }
    }
    
    /**
     * 消费者
     */
    class Consumer implements Runnable {
        private Resource resource;
    
        Consumer(Resource res) {
            this.resource = res;
        }
    
        @Override
        public void run() {
            while (true) {
                resource.get();
            }
        }
    }
    
    public class StarTest {
        public static void main(String[] args) {
            Resource resource = new Resource();
            new Thread(new Producer(resource), "线程1").start();
            new Thread(new Consumer(resource), "线程2").start();
        }
    }
    

    运行结果:
    在这里插入图片描述
    此时运行状态正常

    2、多个生产、消费者

    生产者、消费者各加一个,改动 main() 函数:

    public class StarTest {
        public static void main(String[] args) {
            Resource resource = new Resource();
            new Thread(new Producer(resource), "线程1").start();
            new Thread(new Consumer(resource), "线程2").start();
            new Thread(new Producer(resource), "线程3").start();
            new Thread(new Consumer(resource), "线程4").start();
        }
    }
    

    运行结果:
    在这里插入图片描述在这里插入图片描述
    我们可以看到,此时线程之间通信出现了问题,甚至出现了负数,如图:
    在这里插入图片描述
    要想解决现在的问题,就需要线程每次被唤醒之后都要去判断标识flag,并且避免唤醒己方线程

    改造代码:

    //生产
    public synchronized void set(String name) {
        if (flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.name = name;
        System.out.println(Thread.currentThread().getName() + "---生产者生产---" + this.name + ",剩余数量:" + (++count));
        flag = true;
        notify();
    }
    
    //消费
    public synchronized void get() {
        if (!flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "******消费者消费******" + this.name + ",剩余数量:" + (--count));
        flag = false;
        notify();
    }
    

    改为

    //生产
    public synchronized void set(String name) {
        while (flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.name = name;
        System.out.println(Thread.currentThread().getName() + "---生产者生产---" + this.name + ",剩余数量:" + (++count));
        flag = true;
        notifyAll();
    }
    
    //消费
    public synchronized void get() {
        while (!flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "******消费者消费******" + this.name + ",剩余数量:" + (--count));
        flag = false;
        notifyAll();
    }
    

    运行结果:
    在这里插入图片描述
    此时结果正常

    JDK5实现生产消费者

    JDK5是java的一个里程碑,提供了很多解决方案,比如多线程中加入了Lock锁,我们可以用Lock锁来替换 synchronized ,API中给我们提供了使用方法:
    在这里插入图片描述

    改造代码为lock锁:

    package com.star.test;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    class Resource {
        //资源名称
        private String name;
        //数量
        private int count = 0;
        //标识
        private boolean flag = false;
        //创建lock
        private Lock lock = new ReentrantLock();
        //获取Condition
        private Condition producer_condition = lock.newCondition();
        private Condition consumer_condition = lock.newCondition();
    
        //生产
        public void set(String name) throws InterruptedException {
            //加锁
            lock.lock();
            try {
                while (flag) {
                    //生产者等待
                    producer_condition.await();
                }
                this.name = name;
                System.out.println(Thread.currentThread().getName() + "---生产者生产---" + this.name + ",剩余数量:" + (++count));
                flag = true;
                //唤醒消费者
                consumer_condition.signalAll();
            } finally {
                //释放锁
                lock.unlock();
            }
        }
    
        //消费
        public void get() throws InterruptedException {
            lock.lock();
            try {
                while (!flag) {
                    //消费者等待
                    consumer_condition.await();
                }
                System.out.println(Thread.currentThread().getName() + "******消费者消费******" + this.name + ",剩余数量:" + (--count));
                flag = false;
                //唤醒生产者
                producer_condition.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }
    
    /**
     * 生产者
     */
    class Producer implements Runnable {
        private Resource resource;
    
        Producer(Resource res) {
            this.resource = res;
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    resource.set("衣服");
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
        }
    }
    
    /**
     * 消费者
     */
    class Consumer implements Runnable {
        private Resource resource;
    
        Consumer(Resource res) {
            this.resource = res;
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    resource.get();
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
        }
    }
    
    public class StarTest {
        public static void main(String[] args) {
            Resource resource = new Resource();
            new Thread(new Producer(resource), "线程1").start();
            new Thread(new Consumer(resource), "线程2").start();
            new Thread(new Producer(resource), "线程3").start();
            new Thread(new Consumer(resource), "线程4").start();
        }
    }
    

    运行结果:
    在这里插入图片描述

    线程通信方法

    1、wait()、notify()、notifyAll()

    wait()、notify()、notifyAll() 这些方法都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。但是这些方法都定义在Object类中,因为这些方法在操作同步中线程时,都必须要标识所操作线程持有的锁,只有同一个锁上的被等待线程可以被同一个锁的 notify 唤醒,不可以对不同锁中的线程唤醒。也就是说:等待和唤醒必须是同一个锁

    锁可以是任意对象,可以被任意对象调用的方法定义在 Object 类中

    JDK5中引入了Lock锁,使用 Condition 对象中的 await()、signal()、signalAll() 将 Object 中的 wait()、notify()、notifyAll() ,该对象可以使用 lock 锁进行获取

    2、stop()、interrupt()

    在这里插入图片描述
    stop()方法是不安全的,所以已被弃用,那么如何来停止一个线程?其实我们都知道:run()方法结束就代表线程结束,多线程运行代码通常都是循环结构,只要控制住循环,就可以让 run() 结束,也就是线程结束。但是当线程处于冻结状态时,线程不会结束,如果没有指定方式让冻结线程恢复到运行状态,就需要对冻结线程进行清除,强制让线程恢复到运行状态,此时可以操作标识让线程结束,Thread 类中提供了 interrupt():
    在这里插入图片描述

    3、join()

    当A线程执行到了B线程的 join() 方法时,A线程就放弃运行资格,处于冻结等待状态,等B线程执行完,A才恢复运行资格;如果B线程执行过程中挂掉,那么需要用 interrupt() 方法来清理A线程的冻结状态;join()可以用来临时加入线程执行。

    4、yield()

    暂时释放执行资格,稍微减缓线程切换的频率,让多个线程得到运行资格的机会均等一些

    知识点

    1、wait与sleep区别

    1、sleep()方法属于Thread类的,而wait()方法是Object类中的
    2、sleep()方法使程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态,在调用sleep()方法时,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify() 方法后本线程才进入对象锁定池准备获取对象锁进入运行状态

    2、Lock接口与synchronized关键字的区别

    1、Lock 接口可以尝试非阻塞地获取锁 当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。
    2、Lock 接口能被中断地获取锁, 与 synchronized 不同,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。
    3、Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回

    展开全文
  • 主要介绍了Java编程线程间通信与信号量代码示例,具有一定借鉴价值,需要的朋友可以参考下。
  • 主要介绍了python基于event实现线程间通信控制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要介绍了Python多线程编程(八):使用Event实现线程间通信,,需要的朋友可以参考下
  • 主要介绍了Java管道流实现线程间通信过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • java线程间通信、线程池

    千次阅读 2019-08-22 19:38:34
    文章目录第四章:线程状态4.1 线程状态概述4.2 Waiting (无限等待)和线程间通信4.3 线程间通信代码实战第五章:线程池5.1 线程池5.2 线程池的使用 第四章:线程状态 4.1 线程状态概述 当线程被创建并启动以后,它既...
  • 多线程线程间通信,可用于控制线程内部函数,做处理......................................................
  • 线程间通信方式

    千次阅读 2014-02-03 22:23:43
    线程间通信方式 收藏  线程间通信可以通过下列三种方法: 1)使用全局变量实现线程间通信 2)使用消息实现线程间通信 3)使用CEvent类实现线程间通信  使用全局变量实现线程间通信:  定义一个...
  • 主要介绍了Java编程之多线程死锁与线程间通信简单实现代码,具有一定参考价值,需要的朋友可以了解下。
  • 通过自定义Handler、Looper、Message.浅析Android线程间通信原理
  • Android进程间通信和线程间通信

    千次阅读 2017-02-28 16:36:41
    进程间和线程间通信这个知识点算是高级知识点,线程和进程首先要区分线程和进程的区别: 线程是cup最小调度单元; 进程是一系列线程的集合。进程间方式 Bundle 文件共享 AIDL Messenger,注意不是Message ...
  • C例子:线程间通信

    2016-01-24 23:28:05
    该程序是我写的博客“一起talk C栗子吧(第一百一十一回:C语言实例--线程间通信)”的配套程序,共享给大家使用
  • threadproxy.nim:简化Nim线程间通信

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 208,801
精华内容 83,520
关键字:

线程间通信