博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WebRTC 的 PROXY - 如何解决应用中的线程乱入
阅读量:4182 次
发布时间:2019-05-26

本文共 5316 字,大约阅读时间需要 17 分钟。

是否要保证线程安全是底层SDK设计中的一个问题。    通常来说,   API 线程安全是其易用的重要标志,   但是底层SDK往往是一个比较复杂的状态机,  如果声明了线程安全, 随之而来的是API的调用可能会来自任意线程。   现在的开发应用环境都已经普遍支持线程池, 对应用而言, 把任务交给线程池处理是比较方便的, 也比较自然。  这样的话, 对于底层API的调用可能是直接来自线程池的任意一个线程,  如果真的允许线程乱入,  那对于SDK自身状态维护,  是极大的挑战,  一般也很难处理好。  

       那么应该怎么在保持SDK 易用性的前提下, 同时又能让自己的状态维护尽量保持简单呢?  很容易想到的方法是转线程调用, 将来自任意线程的API调用, 转向SDK内部的工作线程,  这样,在应用的角度看来, 这个SDK是线程安全并且支持线程乱入的, 但是内部又保持了简单有序的操作。    在这里, WebRTC提供了一个极好的设计思路,  那就是PROXY。  

      WebRTC所暴露的native 接口的最重要的模块大概是PeerConnectionFactoryInterface和PeerConnectionInterface,   这两个接口都有各自对应的 PROXY.     让我们以PeerConnectionFactory的实现来分析PROXY的设计思路吧.  且看代码: 

[cpp]   
 
  1. BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)  
  2.   PROXY_METHOD1(void, SetOptions, const Options&)  
  3.   // Can't use PROXY_METHOD5 because unique_ptr must be moved.  
  4.   // TODO(tommi,hbos): Use of templates to support unique_ptr?  
  5.   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(  
  6.       const PeerConnectionInterface::RTCConfiguration& a1,  
  7.       const MediaConstraintsInterface* a2,  
  8.       std::unique_ptr<cricket::PortAllocator> a3,  
  9.       std::unique_ptr<DtlsIdentityStoreInterface> a4,  
  10.       PeerConnectionObserver* a5) override {  
  11.     return signaling_thread_  
  12.         ->Invoke<rtc::scoped_refptr<PeerConnectionInterface>>(  
  13.             rtc::Bind(&PeerConnectionFactoryProxy::CreatePeerConnection_ot,  
  14.                       this, a1, a2, a3.release(), a4.release(), a5));  
  15.   }  
  16.   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(  
  17.       const PeerConnectionInterface::RTCConfiguration& a1,  
  18.       std::unique_ptr<cricket::PortAllocator> a3,  
  19.       std::unique_ptr<DtlsIdentityStoreInterface> a4,  
  20.       PeerConnectionObserver* a5) override {  
  21.     return signaling_thread_  
  22.         ->Invoke<rtc::scoped_refptr<PeerConnectionInterface>>(  
  23.             rtc::Bind(&PeerConnectionFactoryProxy::CreatePeerConnection_ot,  
  24.                       this, a1, a3.release(), a4.release(), a5));  
  25.   }  
  26.   PROXY_METHOD1(rtc::scoped_refptr<MediaStreamInterface>,  
  27.                 CreateLocalMediaStream, const std::string&)  
  28.   PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,  
  29.                 CreateAudioSource, const MediaConstraintsInterface*)  
  30.   PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,  
  31.                 CreateAudioSource,  
  32.                 const cricket::AudioOptions&)  
  33.   PROXY_METHOD2(rtc::scoped_refptr<VideoTrackSourceInterface>,  
  34.                 CreateVideoSource,  
  35.                 cricket::VideoCapturer*,  
  36.                 const MediaConstraintsInterface*)  
  37.   PROXY_METHOD1(rtc::scoped_refptr<VideoTrackSourceInterface>,  
  38.                 CreateVideoSource,  
  39.                 cricket::VideoCapturer*)  
  40.   PROXY_METHOD2(rtc::scoped_refptr<VideoTrackInterface>,  
  41.                 CreateVideoTrack,  
  42.                 const std::string&,  
  43.                 VideoTrackSourceInterface*)  
  44.   PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>,  
  45.                 CreateAudioTrack, const std::string&,  AudioSourceInterface*)  
  46.   PROXY_METHOD2(bool, StartAecDump, rtc::PlatformFile, int64_t)  
  47.   PROXY_METHOD0(void, StopAecDump)  
  48.   PROXY_METHOD1(bool, StartRtcEventLog, rtc::PlatformFile)  
  49.   PROXY_METHOD2(bool, StartRtcEventLog, rtc::PlatformFile, int64_t)  
  50.   PROXY_METHOD0(void, StopRtcEventLog)  
  51.   
  52.  private:  
  53.   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection_ot(  
  54.       const PeerConnectionInterface::RTCConfiguration& a1,  
  55.       const MediaConstraintsInterface* a2,  
  56.       cricket::PortAllocator* a3,  
  57.       DtlsIdentityStoreInterface* a4,  
  58.       PeerConnectionObserver* a5) {  
  59.     std::unique_ptr<cricket::PortAllocator> ptr_a3(a3);  
  60.     std::unique_ptr<DtlsIdentityStoreInterface> ptr_a4(a4);  
  61.     return c_->CreatePeerConnection(a1, a2, std::move(ptr_a3),  
  62.                                     std::move(ptr_a4), a5);  
  63.   }  
  64.   
  65.   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection_ot(  
  66.       const PeerConnectionInterface::RTCConfiguration& a1,  
  67.       cricket::PortAllocator* a3,  
  68.       DtlsIdentityStoreInterface* a4,  
  69.       PeerConnectionObserver* a5) {  
  70.     std::unique_ptr<cricket::PortAllocator> ptr_a3(a3);  
  71.     std::unique_ptr<DtlsIdentityStoreInterface> ptr_a4(a4);  
  72.     return c_->CreatePeerConnection(a1, std::move(ptr_a3), std::move(ptr_a4),  
  73.                                     a5);  
  74.   }  
  75.   END_SIGNALING_PROXY()  

BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)  

END_SIGNALING_PROXY()     这两句声明了PeerConnectionFactoryProxy 对象。  

那么什么叫作SIGNALING_PROXY呢?      还是需要继续分析代码:

[cpp]   
 
  1. #define BEGIN_SIGNALING_PROXY_MAP(c)                                     \  
  2.   class c##Proxy : public c##Interface {                                  \  
  3.    protected:                                                             \  
  4.     typedef c##Interface C;                                               \  
  5.     c##Proxy(rtc::Thread* signaling_thread, C* c)                         \  
  6.       : signaling_thread_(signaling_thread), c_(c) {}                     \  
  7.     ~c##Proxy() {                                                         \  
  8.       MethodCall0<c##Proxy, void> call(                                   \  
  9.           this, &c##Proxy::Release_s);                                    \  
  10.       call.Marshal(signaling_thread_);                                    \  
  11.     }                                                                     \  
  12.                                                                           \  
  13.    public:                                                                \  
  14.     static rtc::scoped_refptr<C> Create(rtc::Thread* signaling_thread, C* c) { \  
  15.       return new rtc::RefCountedObject<c##Proxy>(                         \  
  16.           signaling_thread, c);                                           \  
  17.     }  

这里可以看到,  BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)实际上声明了class,  其名字为:  PeerConnectionFactoryProxy,  继承自接口 PeerConnectionInterface,   为啥要继承PeerConnectionInterface呢,  道理很简单, 因为这个class是PeerConnectionFactoryProxy的代码, 当然需要实现PeerConnectionInterface所定义的借口。 PeerConnectionFactoryProxy 的构造函数需要传入一个参数: signaling_thread,  这个参数是rtc::Thread类型,  它非常关键, PeerConnectionFactory所有跟Signal相关的函数调用都会被切换到这个signaling_thread来调用.    其实现看这里:

[cpp]   
 
  1. class SynchronousMethodCall  
  2.     : public rtc::MessageData,  
  3.       public rtc::MessageHandler {  
  4.  public:  
  5.   explicit SynchronousMethodCall(rtc::MessageHandler* proxy)  
  6.       : e_(), proxy_(proxy) {}  
  7.   ~SynchronousMethodCall() {}  
  8.   
  9.   void Invoke(rtc::Thread* t) {  
  10.     if (t->IsCurrent()) {  
  11.       proxy_->OnMessage(NULL);  
  12.     } else {  
  13.       e_.reset(new rtc::Event(falsefalse));  
  14.       t->Post(this, 0);  
  15.       e_->Wait(rtc::Event::kForever);  
  16.     }  
  17.   }  

SynchronousMethodCall 是这种signaling_proxy的核心实现部分,  它检查外部的调用是否来自signaling_thread, 是则执行, 否则往signaling_thread发出事件, 要求执行, 等待执行完后返回。   这里的实现逻辑虽然简单, 但是却极为有效, 对于普通对象来说 只需要简单声明一些宏,就可以生成对应的proxy, 就保证了对外线程安全,允许乱入, 内部的操作是在单线程有序执行。  真是了不起!

转载地址:http://pczoi.baihongyu.com/

你可能感兴趣的文章
【java小程序实战】swagger2构建restful接口测试
查看>>
【java小程序实战】小程序注册功能的前后端代码
查看>>
【小程序实战代码】小程序登录前后端代码实现
查看>>
【java小程序实战】redis缓存session的实现
查看>>
【FAQ问题记录】创建文件夹时报错java.io.FileNotFoundException:(系统找不到指定的路径。)
查看>>
【java小程序实战】小程序注销功能实现
查看>>
【java小程序】上传头像的功能实现。
查看>>
获取所有checkbox选中的数据,前端传一个对象的数组到后端进行接收。
查看>>
【java小程序】背景页面到小程序的展示
查看>>
【java小程序】小程序视频上传文件的前后端代码
查看>>
【java小程序】使用ffmpeg进行视频与背景音乐的整合
查看>>
【java小程序】利用ffmpeg对视频进行截图操作
查看>>
【java小程序】分页显示视频列表
查看>>
关于request.getServletPath(),request.getContextPath()的总结
查看>>
aop详解和基于spring-aop xml的简单编程
查看>>
【java小程序】zookeeper监听并自动更新
查看>>
软碟通系统U盘制作教程
查看>>
【java多线程编程】三种多线程的实现方式
查看>>
【java多线程】线程常用操作方法总结
查看>>
【java多线程】线程的同步与死锁
查看>>