精华内容
下载资源
问答
  • 操作系统的主要任务是管理计算机的软件、硬件资源。...因此多进程和多线程间为了完成一定的任务,就需要进行一定的通信。而线程间通信又和进程间的通信不同。由于进程的数据空间相对独立而线程是共享数据空间的,

    操作系统的主要任务是管理计算机的软件、硬件资源。现代操作系统的主要特点是多用户和多任务,也就是程序的并行执行,windows如此linux也是如此。所以操作系统就借助于进程来管理计算机的软、硬件资源,支持多任务的并行执行。要并行执行就需要多进程、多线程。因此多进程和多线程间为了完成一定的任务,就需要进行一定的通信。而线程间通信又和进程间的通信不同。由于进程的数据空间相对独立而线程是共享数据空间的,彼此通信机制也很不同。

             线程间通信:由于多线程共享地址空间和数据空间,所以多个线程间的通信是一个线程的数据可以直接提供给其他线程使用,而不必通过操作系统(也就是内核的调度)。

             进程间的通信则不同,它的数据空间的独立性决定了它的通信相对比较复杂,需要通过操作系统。以前进程间的通信只能是单机版的,现在操作系统都继承了基于套接字(socket)的进程间的通信机制。这样进程间的通信就不局限于单台计算机了,实现了网络通信。

            进程的通信机制主要有:管道、有名管道、消息队列、信号量、共享空间、信号、套接字。

            管道:它传递数据是单向性的,只能从一方流向另一方,也就是一种半双工的通信方式;只用于有亲缘关系的进程间的通信,亲缘关系也就是父子进程或兄弟进程;没有名字并且大小受限,传输的是无格式的流,所以两进程通信时必须约定好数据通信的格式。管道它就像一个特殊的文件,但这个文件之存在于内存中,在创建管道时,系统为管道分配了一个页面作为数据缓冲区,进程对这个数据缓冲区进行读写,以此来完成通信。其中一个进程只能读一个只能写,所以叫半双工通信,为什么一个只能读一个只能写呢?因为写进程是在缓冲区的末尾写入,读进程是在缓冲区的头部读取,他们各自
    的数据结构不同,所以功能不同。

            有名管道:看见这个名字就能知道个大概了,它于管道的不同的是它有名字了。这就不同与管道只能在具有亲缘关系的进程间通信了。它提供了一个路径名与之关联,有了自己的传输格式。有名管道和管道的不同之处还有一点是,有名管道是个设备文件,存储在文件系统中,没有亲缘关系的进程也可以访问,但是它要按照先进先出的原则读取数据。同样也是单双工的。

            消息队列:是存放在内核中的消息链表,每个消息队列由消息队列标识符标识,于管道不同的是,消息队列存放在内核中,只有在内核重启时才能删除一个消息队列,内核重启也就是系统重启,同样消息队列的大小也是受限制的。

            信号量:也可以说是一个计数器,常用来处理进程或线程同步的问题,特别是对临界资源的访问同步问题。临界资源:为某一时刻只能由一个进程或线程操作的资源,当信号量的值大于或等于0时,表示可以供并发进程访问的临界资源数,当小于0时,表示正在等待使用临界资源的进程数。更重要的是,信号量的值仅能由PV操作来改变。

            共享内存:就是分配一块能被其他进程访问的内存。共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。首先说下在使用共享内存区前,必须通过系统函数将其附加到进程的地址空间或说为映射到进程空间。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到 进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互
    斥锁和信号量都可以。采用共享内存通信的一个显而易 见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而 共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就 解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存 中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

             信号:信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。信号事件的发生有两个来源:硬件来源(比如我们按下了键盘或者其它硬件故障);软件来源。信号分为可靠信号和不可靠信号,实时信号和非实时信号。进程有三种方式响应信号1.忽略信号2.捕捉信号3.执行缺省操作。

            套接字:这一块在网络编程那一块讲的 很多,在此就不在说拉。

    **********************************************************************************************************************

    windows下多线程通信方法

    多线程知识简介

    同一进程中可以包含多个线程,由于进程中的多个线程可以共享进程中的资源,所以使同一进程中的多个线程之间通信相对比较简单。

    当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前加上volatile声明,来告诉编译器这个全局变量是“易变”(更直接的讲是“直接存取原始内存地址”,更明确的说是不要编辑器去读缓存中的数据,而是直接从内存中获取变量的值)的,让编译器不要对这个变量进行优化。

    使用多线程相对于多进程来说有很多优点:

    • ① 无需跨进程边界;
    • ② 程序逻辑和控制方式简单;
    • ③ 所有线程可以直接共享内存和变量等;
    • ④ 线程方式消耗的总资源比进程方式好;

       

    多线程通信的方法主要有以下三种: 

    1.全局变量

    由于同一进程下的线程之间共享数据空间。当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前加上volatile声明,以防编译器对此变量进行优化。

    2.Message消息机制
    常用的Message通信的接口主要有两个:PostMessage和PostThreadMessage,
    PostMessage为线程向主窗口发送消息。而PostThreadMessage是任意两个线程之间的通信接口。

    PostMessage() 
    函数原型:
        B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);

    参数:
        hWnd:其窗口程序接收消息的窗口的句柄。可取有特定含义的两个值:
        HWND.BROADCAST:消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口
    和弹出式窗口。消息不被寄送到子窗口。
        NULL:此函数的操作和调用参数dwThread设置为当前线程的标识符PostThreadMessage函数一样。
        Msg:指定被寄送的消息。
        wParam:指定附加的消息特定的信息。
        IParam:指定附加的消息特定的信息。
        返回值:如果函数调用成功,返回非零值:如果函数调用失败,返回值是零。
    MS还提供了SendMessage方法进行消息间通讯,SendMessage(),他和PostMessage的区别是:

    SendMessage是同步的,而PostMessage是异步的。SendMessage必须等发送的消息执行之后,才返回。
    PostThreadMessage()

    PostThreadMessage方法可以将消息发送到指定线程。
    函数原型:BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam, LPARAM lParam);

    参数除了ThreadId之外,基本和PostMessage相同。
    目标线程通过GetMessage()方法来接受消息。

    注:使用这个方法时,目标线程必须已经有自己的消息队列。否则会返回ERROR_INVALID_THREAD_ID错误。可以用
    PeekMessage()给线程创建消息队列。

    3.CEvent对象

    CEvent为MFC中的一个对象,可以通过对CEvent的触发状态进行改变,从而实现线程间的通信和同步,这个主要是实现线程直接同步的一种方法。

    ************************************************************************

    Linux 多线程通信

    摘自资料(linux 与Windows不同)

         线程间无需特别的手段进行通信,因为线程间可以共享数据结构,也就是一个全局变量可以被两个线程同时使用。不过要注意的是线程间需要做好同步,一般用mutex。可以参考一些比较新的UNIX/Linux编程的书,都会提到Posix线程编程,比如《UNIX环境高级编程(第二版)》、《UNIX系统编程》等等。 linux的消息属于IPC,也就是进程间通信,线程用不上。

    linux用pthread_kill对线程发信号。 另:windows下不是用post..(你是说PostMessage吗?)进行线程通信的吧?

    windows用PostThreadMessage进行线程间通信,但实际上极少用这种方法。还是利用同步多一些 LINUX下的同步和Windows原理都是一样的。不过Linux下的singal中断也很好用。

    用好信号量,共享资源就可以了。

     使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。

      使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
    1、简单的多线程程序

       首先在主函数中,我们使用到了两个函数,pthread_create和pthread_join,并声明了一个pthread_t型的变量。
    pthread_t在头文件pthread.h中已经声明,是线程的标示符

       函数pthread_create用来创建一个线程,函数原型:

    extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t*__attr,void *(*__start_routine) (void *), void *__arg));

      第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。若我们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。

    函数pthread_join用来等待一个线程的结束。函数原型为:

      extern int pthread_join __P ((pthread_t __th, void **__thread_return));

      第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。它的函数原型为:

      extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));

      唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。

    2、修改线程的属性
    设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线程。

    #include 
    pthread_attr_t attr;
    pthread_t tid;

    /*初始化属性值,均设为默认值*/
    pthread_attr_init(&attr);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

    pthread_create(&tid, &attr, (void *) my_function, NULL);

    3、线程的数据处理

    和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个拷贝(除非使用不同的数据段)。在函数中声明的静态变量常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字volatile来定义,这是为了防止编译器在优化时(如gcc中使用-OX参数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。

    4、互斥锁

    互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的


    展开全文
  • Android多线程通信机制

    千次阅读 2018-08-07 16:48:58
    掌握Android的多线程通信机制,我们首先应该掌握Android中进程与线程是什么。 1. 进程 在Android中,一个应用程序就是一个独立的进程(应用运行在一个独立的环境中,可以避免其他应用程序/进程的干扰)。一般来说...

    掌握Android的多线程通信机制,我们首先应该掌握Android中进程与线程是什么。
    1. 进程
    在Android中,一个应用程序就是一个独立的进程(应用运行在一个独立的环境中,可以避免其他应用程序/进程的干扰)。一般来说,当我们启动一个应用程序时,系统会创建一个进程(从Zygote中fork出来的,这个进程会有独立的ID),并为这个进程创建一个主线程(UI线程),然后就可以运行MainActivity了,应用程序的组件默认都是运行在它的进程中,但我们可以通过指定应用的组件(四大组件)的运行进程:android:process来让组件运行在不同的进程中;让组件运行在不同的进程中,会带来好处,也会带来坏处:
    好处是:因为每一个应用程序(也就是每一个进程)都会有一个内存预算,所有运行在这个这个进程中的程序使用的总内存不能超过这个值,让组件运行不同的进程中,可以让主进程可以拥有更多的空间资源。
    坏处是:每个进程都会有自己的虚拟机实例,因此让在进程间共享一些数据变得困难;(当然,我们可以采用多进程间的通信来实现数据的共享)
    当我们的应用程序比较大,需要的内存资源比较多时(也就是用户会抱怨应用经常出现OutOfMemory时),可以考虑使用多进程
    2. 线程
    在Java中,线程会有那么几种状态:创建,就绪,运行,阻塞,死亡。当应用程序有组件在运行时,UI线程是处于运行状态的。默认情况下,应用的所有组件的操作都是在UI线程里完成的,包括响应用户的操作(触摸,点击等),组件生命周期方法的调用,UI的更新等。因此如果UI线程处理阻塞状态时(在线程里做一些耗时的操作,如网络连接等),就会不能响应各种操作,如果阻塞时间达到5秒,就会让程序处于ANR(application not response)状态,这时用户就可能退出你的应用甚至卸载你的应用,这是我们不能接受的。 这时,有人就会说,我们在其他线程中更新UI不就可以了吗,就不会导致UI线程处于阻塞状态了。但答案是否定的。

    因为Android的UI线程是非线程安全的,应用更新UI,是调用invalidate()方法来实现界面的重绘,而invalidate()方法是非线程安全的,也就是说当我们在非UI线程来更新UI时,可能会有其他的线程或UI线程也在更新UI,这就会导致界面更新的不同步。因此我们不能在非UI主线程中做更新UI的操作。也就是说我们在使用Android中的线程时,要保证:
    不能阻塞UI主线程,也就是不能在UI主线程中做耗时的操作,如网络连接,文件的IO;
    只能在UI主线程中做更新UI的操作;
    在Android中,我们把除UI线程外的,其他所有的线程都叫做工作线程,也就是说Android只会存在两种线程:UI主线程(UI thread)和工作线程(work thread).我们不能在UI主线程中做耗时的操作,因此我们可以把耗时的操作放在另一个工作线程中去做。操作完成后,再通知UI主线程做出相应的响应。这就需要掌握线程间通信的方式了。在Android中提供了两种线程间的通信方式:一种是AsyncTask机制,另一种是Handler机制

    Android线程间通信方式之Handler机制;
    使用Handler机制,也就是通过使用Handler,Looper,MessageQueue,和Message这几个类协调来完成。我们先来看使用Handler机制完成线程间通信的原理,然后再详细介绍这几个类;先看一下这几个类的作用:
    Handler,发送和处理Message对象和Runnable对象;
    Looper,用于从MessageQueue中取出消息(Message)对象,并发送给Handler处理;
    MessageQueue,消息队列,用于存放通过Handler发布的消息;
    Message,消息的类型,里面包含几个实例对象:
    what,用户定义的int型消息代码,用来描述消息;
    obj,随消息发送的用户用户指定对象;
    target,处理消息的Handler;
    一个Handler对象仅与一个Looper相关联,一个Message也仅与一个目标Handler对象相关联,一个Looper对象拥有一个MessageQueue。但多个不同的Handler对象可以与同一个对象相关联,也就是说多个Handler可以共享一个MessageQueue,从而达到消息共享的目的,这也是Android通过Handler机制实现多线程间通信的核心原理;
    上面说到Handler对象仅与一个Looper相关联,那么这个关联是什么时候实现的呢?答案是:Handler对象创建的时候。UI主线程在创建的时候就会拥有一个handler对象和一个Looper对象(工作线程需要自己调用Looper.prepare()来创建一个Looper对象),然后任何在UI主线程中创建的Handler对象默认都会与UI主线程的Looper对象相关联(Handler对象创建的时候,会与这个线程的Looper对象相关联)。进而我们就可以把在UI主线程中创建的Handler对象传递给(依赖注入或引用形式)工作线程,那么在工作线程中用这个Handler对象处理的消息就是在UI主线程的MessageQueue中处理的,从而达到线程间通信的目的。
    了解了使用Handler机制来实现Android线程间异步通信的原理,下面我们再来详细了解下这四个核心类;
    2.2.1 Handler
    Handler,继承自Object类,用来发送和处理Message对象或Runnable对象;Handler在创建时会与当前所在的线程的Looper对象相关联(如果当前线程的Looper为空或不存在,则会抛出异常,此时需要在线程中主动调用Looper.prepare()来创建一个Looper对象)。使用Handler的主要作用就是在后面的过程中发送和处理Message对象和让其他的线程完成某一个动作(如在工作线程中通过Handler对象发送一个Message对象,让UI线程进行UI的更新,然后UI线程就会在MessageQueue中得到这个Message对象(取出Message对象是由其相关联的Looper对象完成的),并作出相应的响应)。
    Handler用post体系来完成发送Runnable对象的工作,用sendMessage体系 来完成发送Message对象的工作;
    post体系,允许把一个Runnable对象发送到消息队列中,它的方法有:post(Runnable),postDelayed(Runnable,long),postAtTime(Runnable,long);
    sendMessage体系,把Message对象发送给消息队列,它的方法有:sendEmptyMessage(int),sendMessage(Message),sendMessageDelayed(Message,long),sendMessageAtTime(Message,long);
    如果Handler是通过post体系将Runnable对象发送到MessageQueue队列中,则这个Runnable对象的run()方法是运行在Handler对象创建时所在线程;
    如果Handler是通过sendMessage体系将Message发送到MessageQueue中,则需要重写handleMessage()方法来获取工作线程传递过来的Message对象,handleMessage()方法是工作在Handler对象建立时所在的线程的(一般我们会在UI线程中建立Handler对象,然后传递给子线程,此时这个Handler对象的handleMessage()方法就是运行在UI主线程中的);
    2.2.2 Message
    Message用来定义一个包含任意数据的消息对象,这个消息对象是可以被发送给Handler处理的。我们最好通过Message.obtain()和Handler.obtatinMessage()来得到一个Message对象(通过这两个方法得到的对象是从对象回收池中得到,也就是说是复用已经处理完的Message对象,而不是重新生成一个新对象),如果通过Message的构造方法得到一个Message对象,则这个Message对象是重新生成的(不建议使用这种方法)。
    Message对象用来封装需要传递的消息,Message的数据结构为:
    Message{
    int arg1;//如果我们只需要存储一些简单的Integer数据,则可通过设置这个属性来传递
    int agr2;//使用同arg1
    Object obj; //设置需要发送给接收方的对象,这个对象需要实现序列化接口
    int what; //描述这个消息的标识;
    //设置与这个消息对应的任意数据,这个数据是用Bundle封装的;
    void setData(Bundle data);
    Bundle getData(); 得到与这个消息对应的数据信息;
    //省略了方法和可选的属性
    ……
    如果需要通过Message对象传递一些比较复杂的数据,则需要使用将数据封装成Bundle对象,然后通过setData(Bundle)方法来传递,用getData()来得到与这个消息对应的数据(这方法与设置Message的Object 属性作用相同);
    2.2.3 MessageQueue
    MessageQueue保存由Looper调度的消息列表,消息通过与Looper相关联的Handler对象添加进MessageQueue。
    2.2.4 Looper
    Looper为线程运行一个消息的循环队列,主要就是为了完成MessageQueue与Handler交互的功能;需要注意的是线程默认并不会给我们提供一个一个Looper实例来管理消息队列,我们需要在线程中主动调用Looper.prepare()方法来实例化一个Looper对象,用于管理消息队列;Looper对象会不断去判断MessageQueue是否为空,如果不空,则将Message取出给相应的Handler进行处理;如果MessageQueue为空,则Looper对象会进行阻塞状态,直到有新的消息进入MessageQueue;
    其实,说白了,Android中通过Handler机制来异步处理多线程间的通信就是多个线程间共享一个MessageQueue,工作线程将消息发送到MessageQueue,然后UI线程或其他工作线程在MessageQueue在取出消息,进行相应的处理;

    展开全文
  • 写一个类库,即支持多线程通信又支持进程间通信,请各位大神给个思路?
  • C++多线程,多线程通信,队列

    千次阅读 2019-06-25 19:19:18
    条件变量std::condition_variable用于多线程之间的通信,它可以阻塞一个或同时阻塞多个线程。std::condition_variable需要与std::unique_lock配合使用。std::condition_variable效果上相当于包装了pthread库中的...

    1,使用

    
    TQueueConcurrent<std::vector<std::string>> fifo_queue;
    
    ...
    ...
    
    fifo_queue.emplace_back(string_ret);
    LOG(INFO) << "fifo_queue size: " << fifo_queue.size();
    ...
    ...
    
    if(!fifo_queue.is_empty()) {
    	std::vector<std::string> string_ret = fifo_queue.pop_front_not_wait();
    	...
    	...
    	}
    }
    

    2,实现

    template< typename T >
    class TQueueConcurrent {
        using const_iterator = typename std::deque<T>::const_iterator;
     
    public:
        //! \brief Emplaces a new instance of T in front of the deque
        template<typename... Args>
        void emplace_front( Args&&... args )
        {
            addData_protected( [&] {
                _collection.emplace_front(std::forward<Args>(args)...);
            } );
        }
     
        //! \brief Emplaces a new instance of T at the back of the deque
        template<typename... Args>
        void emplace_back( Args&&... args )
        {
            addData_protected( [&] {
                _collection.emplace_back(std::forward<Args>(args)...);
            } );
        }
     
        //! \brief Returns the front element and removes it from the collection
        //!
        //!        No exception is ever returned as we garanty that the deque 
        //is not empty
        //!        before trying to return data.
        T pop_front( void ) noexcept
        {
            std::unique_lock<std::mutex> lock{_mutex};
            while (_collection.empty()) {
                _condNewData.wait(lock);
            }
            auto elem = std::move(_collection.front());
            _collection.pop_front();
            return elem;
        }
        T pop_front_not_wait( void ) noexcept
        {
            std::unique_lock<std::mutex> lock{_mutex};
            auto elem = std::move(_collection.front());
            _collection.pop_front();
            lock.unlock();
            return elem;
        }
        bool is_empty() {
            std::unique_lock<std::mutex> lock{_mutex};
            bool is_empty = _collection.empty();
            lock.unlock();
            return is_empty;
        }
        int size(){
            std::unique_lock<std::mutex> lock{_mutex};
            int size = _collection.size();
            lock.unlock();
            return size;
        }
     
    private:
     
        //! \brief Protects the deque, calls the provided function and 
        //notifies the presence of new data
        //! \param The concrete operation to be used. It MUST be an operation 
        //which will add data to the deque,
        //!        as it will notify that new data are available!
        template<class F>
        void addData_protected(F&& fct)
        {
            std::unique_lock<std::mutex> lock{ _mutex };
            fct();
            lock.unlock();
            _condNewData.notify_one();
        }
     
        std::deque<T> _collection;  ///< Concrete, not thread safe, storage.
        std::mutex   _mutex;        ///< Mutex protecting the concrete storage
        std::condition_variable _condNewData;   ///< Condition used to notify 
                                                ///that new data are available.
    };
    

    3,知识点

    1, std::mutex

    https://blog.csdn.net/u012507022/article/details/85909567

    std::mutex是C++11中最基本的互斥量,std::mutex对象提供了独占所有权的特性,不支持递归地对std::mutex对象上锁。
    
        std::mutex成员函数:
    
        (1)、构造函数:std::mutex不支持copy和move操作,最初的std::mutex对象是处于unlocked状态。
    
        (2)、lock函数:互斥锁被锁定。线程申请该互斥锁,如果未能获得该互斥锁,则调用线程将阻塞(block)在该互斥锁上;如果成功获得该互诉锁,该线程一直拥有该互斥锁直到调用unlock解锁;如果该互斥锁已经被当前调用线程锁住,则产生死锁(deadlock)。
    
        (3)、unlock函数:解锁,释放调用线程对该互斥锁的所有权。
    
        (4)、try_lock:尝试锁定互斥锁。如果互斥锁被其他线程占有,则当前调用线程也不会被阻塞,而是由该函数调用返回false;如果该互斥锁已经被当前调用线程锁住,则会产生死锁。其中std::mutex就是lock、unlock。std::lock_guard与std::mutex配合使用,把锁放到lock_guard中时,mutex自动上锁,lock_guard析构时,同时把mutex解锁。
         (5)、native_handle:返回当前句柄。
    

    2,std::condition_variable

    https://blog.csdn.net/fengbingchun/article/details/73695596

    条件变量std::condition_variable用于多线程之间的通信,它可以阻塞一个或同时阻塞多个线程。std::condition_variable需要与std::unique_lock配合使用。std::condition_variable效果上相当于包装了pthread库中的pthread_cond_*()系列的函数。
    
    当std::condition_variable对象的某个wait函数被调用的时候,它使用std::unique_lock(通过std::mutex)来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的std::condition_variable对象上调用了notification函数来唤醒当前线程。
    
    std::condition_variable对象通常使用std::unique_lock<std::mutex>来等待,如果需要使用另外的lockable类型,可以使用std::condition_variable_any类。
    
    std::condition_variable类的成员函数:
    
    (1)、构造函数:仅支持默认构造函数,拷贝、赋值和移动(move)均是被禁用的。
    
    (2)、wait:当前线程调用wait()后将被阻塞,直到另外某个线程调用notify_*唤醒当前线程;当线程被阻塞时,该函数会自动调用std::mutex的unlock()释放锁,使得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(notify,通常是另外某个线程调用notify_*唤醒了当前线程),wait()函数也是自动调用std::mutex的lock()。wait分为无条件被阻塞和带条件的被阻塞两种。
    
    无条件被阻塞:调用该函数前,当前线程应该已经对unique_lock<mutex> lck完成了加锁。所有使用同一个条件变量的线程必须在wait函数中使用同一个unique_lock<mutex>。该wait函数内部会自动调用lck.unlock()对互斥锁解锁,使得其他被阻塞在互斥锁上的线程恢复执行。使用本函数被阻塞的当前线程在获得通知(notified,通过别的线程调用 notify_*系列的函数)而被唤醒后,wait()函数恢复执行并自动调用lck.lock()对互斥锁加锁。
    
    带条件的被阻塞:wait函数设置了谓词(Predicate),只有当pred条件为false时调用该wait函数才会阻塞当前线程,并且在收到其它线程的通知后只有当pred为true时才会被解除阻塞。因此,等效于while (!pred())  wait(lck).
    
    (3)、wait_for:与wait()类似,只是wait_for可以指定一个时间段,在当前线程收到通知或者指定的时间超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_for返回,剩下的步骤和wait类似。
    
    (4)、wait_until:与wait_for类似,只是wait_until可以指定一个时间点,在当前线程收到通知或者指定的时间点超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_until返回,剩下的处理步骤和wait类似。
    
    (5)、notify_all: 唤醒所有的wait线程,如果当前没有等待线程,则该函数什么也不做。
    
    (6)、notify_one:唤醒某个wait线程,如果当前没有等待线程,则该函数什么也不做;如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)。
    
    展开全文
  • Qt多线程通信

    万次阅读 多人点赞 2018-03-03 11:56:03
    即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的。 2)使用singal/slot机制,把数据从一个线程传递到另外一个线程。 第一种方法在各个编程语言都...

    简述:

    1> Qt线程间共享数据主要有两种方式:

    1)使用共享内存。即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的。

    2)使用singal/slot机制,把数据从一个线程传递到另外一个线程。

    第一种方法在各个编程语言都普遍使用,而第二种方法是QT的特有的,本文主要介绍第二种。

    2 > 槽参数类型

    1) 在线程间使用信号槽进行通信时,槽参数必须使用元数据类型的参数;

    2) Qt内生的元数据类型,如int double QString等;

    3) 如果要用自定义的数据类型,需要在connect之前将其注册qRegisterMetaType)为元数据类型。

    4) 线程间用“信号与槽”传递引用参数的话,要加const,因为const文字常量存在常量区中,生命周期与程序一样的长。这样可以避免slot调用的时候参数的运行期已过而使引用无效。

    connect(m_thread, SIGNAL(MsgSignal(const QString&)),
                  this, SLOT(OnMsgSignal(
    const QString&)));

    3 > Q_DECLARE_METATYPE与qRegisterMetaType 
    Q_DECLARE_METATYPE

    如果要使自定义类型或其他非QMetaType内置类型在QVaiant中使用,必须使用该宏。

    该类型必须有公有的 构造、析构、复制构造函数。

    qRegisterMetaType 

    必须使用该函数的两种情况:

    如果非QMetaType内置类型要在Qt的属性系统中使用。

    如果非QMetaType内置类型要在queued 信号与槽中使用。

    两者的关系:

    Q_DECLARE_METATYPE展开后是一个特化后的类QMetaTypeId<TYPE>

    qRegisterMetaType将某类型注册到MetaType系统中。

    QMetaTypeId<TYPE>的类中成员包含对qRegisterMetaType的调用。

    1、传递int参数(主线程与子线程)


    testthread.h 文件

    #ifndef TESTTHREAD_H  
    #define TESTTHREAD_H  
      
    #include <QThread>  
      
    #include "msg.h"  
      
    class TestThread : public QThread  
    {  
        Q_OBJECT  
    public:  
        explicit TestThread(QObject *parent = 0);  
      
    protected:  
        void run();  
      
    signals:  
        void TestSignal(int);  
      
    private:  
        Msg msg;  
    };  
      
    #endif // TESTTHREAD_H 


    testthread.cpp文件

    #include "testthread.h"  
      
    TestThread::TestThread(QObject *parent) :  
        QThread(parent)  
    {  
    }  
      
    void TestThread::run()  
    {  
        //触发信号  
        emit TestSignal(123);  
    } 

    自定义的类继承了QThread类,重写run函数,然后触发TestSignal信号。

    mainwindow.h

    #ifndef MAINWINDOW_H  
    #define MAINWINDOW_H  
      
    #include <QMainWindow>  
      
    #include "testthread.h"  
      
    namespace Ui {  
    class MainWindow;  
    }  
      
    class MainWindow : public QMainWindow  
    {  
        Q_OBJECT  
      
    public:  
        explicit MainWindow(QWidget *parent = 0);  
        ~MainWindow();  
      
    private slots:  
        void DisplayMsg(int);  
      
    private:  
        Ui::MainWindow *ui;  
        TestThread *t;  
    };  
      
    #endif // MAINWINDOW_H 


    mainwindow.cpp

    #include "mainwindow.h"  
    #include "ui_mainwindow.h"  
      
    MainWindow::MainWindow(QWidget *parent) :  
        QMainWindow(parent),  
        ui(new Ui::MainWindow)  
    {  
        ui->setupUi(this);  
      
        //进行connect前必须实例化  
        t = new TestThread();     
      
        connect(t, SIGNAL(TestSignal(int)), this, SLOT(DisplayMsg(int)));  
      
        //执行子线程  
        t->start();   
    }  
      
    void MainWindow::DisplayMsg(int a)  
    {  
        ui->textBrowser->append(QString::number(a));  
    }  
      
    MainWindow::~MainWindow()  
    {  
        delete ui;  
    } 

    Mainwindow里面连接信号槽,并且将收到的int参数显示在界面上。
    运行效果:



    2、传递自定义参数(主线程与子线程)

    对以上程序进行简单的修改,使它传递自定义消息。

    testthread.h 文件

    #ifndef TESTTHREAD_H  
    #define TESTTHREAD_H  
      
    #include <QThread>  
      
    #include "msg.h"  
      
    class TestThread : public QThread  
    {  
        Q_OBJECT  
    public:  
        explicit TestThread(QObject *parent = 0);  
        Msg msg;  
      
    protected:  
        void run();  
      
    signals:  
        void TestSignal(Msg);   //自定义消息Msg!!!  
    };  
      
    #endif // TESTTHREAD_H 

    testthread.cpp文件

    #include "testthread.h"  
      
    TestThread::TestThread(QObject *parent) :  
        QThread(parent)  
    {  
    }  
      
    void TestThread::run()  
    {  
        msg.int_info = 999;  
        msg.str_info = "Hello Main Thread!";  
        //触发信号  
        emit TestSignal(msg);  
    }  

    mainwindow.h

    #ifndef MAINWINDOW_H  
    #define MAINWINDOW_H  
      
    #include <QMainWindow>  
      
    #include "testthread.h"  
    #include "msg.h"  
      
    namespace Ui {  
    class MainWindow;  
    }  
      
    class MainWindow : public QMainWindow  
    {  
        Q_OBJECT  
      
    public:  
        explicit MainWindow(QWidget *parent = 0);  
        ~MainWindow();  
      
    private slots:  
        void DisplayMsg(Msg);   //Msg!!!  
      
    private:  
        Ui::MainWindow *ui;  
        TestThread *t;  
    };  
      
    #endif // MAINWINDOW_H  


    mainwindow.cpp

    #include "mainwindow.h"  
    #include "ui_mainwindow.h"  
      
    MainWindow::MainWindow(QWidget *parent) :  
        QMainWindow(parent),  
        ui(new Ui::MainWindow)  
    {  
        ui->setupUi(this);  
      
        //进行connect前必须实例化  
        t = new TestThread();  
      
        //Msg!!!  
        connect(t, SIGNAL(TestSignal(Msg)), this, SLOT(DisplayMsg(Msg)));  
      
        //执行子线程  
        t->start();  
    }  
      
    void MainWindow::DisplayMsg(Msg msg)  
    {  
        ui->textBrowser->append(QString::number(msg.int_info));  
        ui->textBrowser->append(msg.str_info);  
    }  
      
    MainWindow::~MainWindow()  
    {  
        delete ui;  
    } 

    此时再进行编译,编译通过,但Qt Creator会有提示:

    QObject::connect: Cannot queue arguments of type 'Msg'  
    (Make sure 'Msg' is registered using qRegisterMetaType().)

    并且运行程序时会发现,信号发送了,槽函数始终不调用。

    如果将槽参数注册为元数据类型,即mainwindow.cpp文件改动一下:

    ui->setupUi(this);  
      
    qRegisterMetaType<Msg>("Msg"); 

    此时便可正常运行:



    3、传递自定义参数(子线程与子线程)


    原理同上,然后把connect函数中的第三参数this(主线程)改成要监听的另一个线程对象就好了(QT多么健壮、友好、强大)。

    connect(t, SIGNAL(TestSignal(Msg)), this, SLOT(DisplayMsg(Msg)));  

    前提是全部的线程都要在主线程里面实例化(new)。


    4、传递自定义结构体参数(子线程与子线程)


    实现子线程与GUI子线程的参数进行传递。

    线程

    头文件 ABFThread.h

    public:  
      
        struct G_ABFTableSrcUnit  
        {  
            int a;  
            int b;  
            int c;  
            float d;  
            float e;  
            unsigned int f;  
            float Gg;  
          
            QString waveformTypel;  
        };  
      
    public slots:  
      
        void parameterPassing(abfThread::G_ABFTableSrcUnit); //线程自己调用自己的结构体。。。必须这么写不然主线程会报错的  错误是参数内容不一样

    ABFThread.cpp

    void abfThread::parameterPassing(abfThread::G_ABFTableSrcUnit)  
    {  
      
    }  

    GUI线程 

    radarControl.h

    #include "abfThread"  
      
    private:  
        Ui::radarControl *ui;  
      
        abfThread::G_ABFTableSrcUnit mst_abfSrcUnit;  
      
      
    signals:  
        void sendString(abfThread::G_ABFTableSrcUnit);
    radarControl.cpp
    按下按钮就发射信号

    void radarControl::on_pushButton_clicked()  
    {  
        emit sendString(mst_abfSrcUnit);  
    }  
    mainwindow.h
    #include "abfThread.h"  
    #include "radarControl.h"
    mainwindow.cpp
    radarInterface = new radarControl();  
    m_ABFThread = new QThread();  
    m_ABF = new abfThread();  
    m_ABF->moveToThread(m_ABFThread);  
    m_ABFThread->start();  
      
    qRegisterMetaType<abfThread::G_ABFTableSrcUnit>("abfThread::G_ABFTableSrcUnit");  
    connect(radarInterface,SIGNAL(sendString(abfThread::G_ABFTableSrcUnit)),m_ABF,SLOT(parameterPassing(abfThread::G_ABFTableSrcUnit)));  
    //除了注册结构体外  还要保证传递的参数写法要一样  这就是为什么 前面线程自己定义的结构体自己调用自己的原因了 


    小结:

    1 > Qt的信号槽函数只默认支持Qt的类型C++提供的内建的基本类型,比如int double float等,根本不支持C++的std::string std::vector 自定义的struct类型。所以需要用Qt提供的Q_DECLARE_METATYPEqRegisterMetaType来声明和注册自定义的类型和C++的其他类型。
    2 > 多线程间的信号槽传递,在connect的时候需要以Qt::QueuedConnection的方式,不然以Qt::DirectConnection的方式接收者UI线程会很长时间收不到后台线程发出的信号,或者信号直接丢失都是有可能的


    展开全文
  • C++ 多线程通信之 wait、notify、condition_variable 学习日志 1、 C++ 11 线程认识 在C++ 11标准库中提供了一种新的线程创建方式: thread t(func, parm); ① t.detach(); ② t.join(); detach 顾名思义即为工作...
  • Java多线程通信:交替打印ABAB

    千次阅读 2018-08-20 16:35:38
    使用wait()和notify()实现Java多线程通信:两个线程交替打印A和B,如ABABAB public class Test { public static void main(String[] args) { final PrintAB print = new PrintAB(); new Thread(new Runnable() ...
  • 1.多线程通信 现代社会崇尚合作精神,分工合作在日常生活和工作中无处不在。举个简单的例子,例如一条生产线的上下两个工序,它们必须以规定的速率完成各自的工作,才能保证产品在流水线中顺利的流转。如果下工序...
  • linux下多线程通信(一)

    千次阅读 2018-09-21 20:11:57
    在linux下进行多线程编程,肯定会涉及到线程通信问题,本文主要分析pipe,即管道在多线之间通信实现。 #include&amp;amp;amp;amp;lt;unistd.h&amp;amp;amp;amp;gt; int pipe(int filedes[2]); 返回值:成功...
  • C++ 多线程通信方式简介并结合生产者-消费者模式样例分析多线程通信1,全局变量2,自定义消息3,std::promise与std::future(c++11)生产者-消费者模式样例分析1,使用场景介绍2,.h声明3,cpp实现 多线程通信 ...
  • 本周的学习计划是Android通信模块,内容:单线程,多线程通信方式,Handler 与UI Thread的交互,Handler接合子线程的使用。 二丶效果演示(源码链接见文末) 三丶实现功能 1.演示使用Handler常见崩溃 2....
  • python3 queue的多线程通信

    千次阅读 2019-01-18 11:56:59
    queue分类 python3 queue分三类: 先进先出队列 后进先出的栈 优先级队列 他们的导入方式分别是: from queue import Queue ...多线程里用queue 设置俩队列,一个是要做的任务队列todo_queue,一个是已...
  • libevent实现多线程 LibEvent代码阅读---线程间通信、信号处理 libevent并不是线程安全的,但这不代表libevent不支持多线程模式。 前几天在微博上看到ruanyf发了条微博说到apache和nginx的并发模型,看到评论...
  • windows下多线程通信方法

    万次阅读 2012-06-12 14:20:16
    多线程知识简介 同一进程中可以包含多个线程,由于进程中的多个线程可以共享进程中的资源,所以使同一进程中的多个线程之间通信相对比较简单。 当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前...
  • 在研究c# 线程之间通信时,发现传统的方法大概有三种 ①全局变量,由于同一进程下的个进程之间共享数据空间,所以使用全局变量是最简单的方法,但要记住使用volatile进行限制。 ②线程之间发送消息(这...
  • java的多线程通信有Lock,wait/notify,Semaphore三种方式,已一道常见面试题来简单演示这三种多线程通信方式。 两个线程循环间隔打印指定内容,一个打印从1到52的数字,一个打印从A到Z的字母,打印输出如下: 1 2 A...
  • JAVA 多线程通信 详解与举例

    万次阅读 2013-05-27 16:11:02
    为了实现线程通信,我们可以使用Object类提供的wait()、notify()、notifyAll()三个方法。调用wait()方法会释放对该同步监视器的锁定。这三个方法必须由同步监视器对象来调用,这可分成两种情况: 对于使用...
  • Linux 多线程通信

    万次阅读 2008-03-13 11:01:00
    摘自资料(linux 与Windows不同) 线程间无需特别的手段进行通信,因为线程间可以共享数据结构,也就是一个全局变量可以被两个线程同时使用。不过要注意的是线程间需要做好同步,一般用mutex。可以参考一些比较新的...
  • 多线程知识简介 同一进程中可以包含多个线程,由于进程中的多个线程可以共享进程中的资源,所以使同一进程中的多个线程之间通信相对比较简单。 当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前...
  • JAVA中基于UDP实现多线程通信

    千次阅读 2017-03-17 15:06:38
    服务器端程序,利用DatagramSocket负责监听端口,当客户端发过来消息时,...并将socket,packet对象发送给子线程,由子线程完成后面的事务,主线程将进行下一次循环,在receive(packet)处阻塞监听客户端的响应。
  • 请大家尊重劳动成果,转载请注明出处:...  Java多线程之间要交换信息,有时只能用管道来完成,在使用管道通信时,经常会碰到“java - IOException: Read end dead”或者“java - IOException: Write end dead”的异
  • Java Socket实战之二 多线程通信

    万次阅读 多人点赞 2012-02-14 21:43:22
    Java Socket实战之一 单线程通信 上一篇文章说到怎样写一个最简单的Java Socket通信,但是在上一篇文章中的例子有一个问题就是Server只能接受一个Client请求,当第一个Client连接后就占据了这个位置,后续Client不...
  • Java Socket实现基于TCP和UDP多线程通信

    千次阅读 多人点赞 2017-05-28 10:12:45
    1.Socket 通信简介及模型 Java Socket 可实现客户端–服务器间的双向实时通信。java.net包中定义的两个类socket和ServerSocket,分别用来实现双向连接的client和server端。通过Socket实现TCP编程1.1 TCP编程  ...
  • 彻底明白多线程通信机制:

    千次阅读 2011-11-02 09:28:04
    线程间的通信 1. 线程的几种状态 线程有四种状态,任何一个线程肯定...2) 可执行(Runnable):每个支持多线程的系统都有一个排程器,排程器会从线程池中选择一个线程并启动它。当一个线程处于可执行状态时,表
  • Java多线程通信机制

    千次阅读 2012-12-26 16:54:34
    Java提供了3个非常重要的方法来巧妙地解决线程间的通信问题。这3个方法分别是:wait()、notify()和notifyAll()。它们都是Object类的最终方法,因此每一个类都默认拥有它们。  虽然所有的类都默认拥有这3个方法,...
  • JavaScript 中的多线程通信

    千次阅读 2014-11-19 23:44:05
    本文参考《Html 5 与 Css 3 权威指南》  在Html 5诞生之后,我们可以使用javascript来实现多线程处理。H5 新增了一个web workers api,使用这个API,用户可以很容易地创建在后台运行的线程,H5 ...
  • Qt多线程通信 (附demo)

    千次阅读 2019-03-15 14:45:45
    简述: 1&gt;...即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的。 2)使用singal/slot机制,把数据从一个线程传递到...
  • Java Socket实现基于TCP多线程通信

    千次阅读 2018-04-18 15:22:42
    TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 420,481
精华内容 168,192
关键字:

多线程通信