网络多线程
我正在编写在线游戏的原因有两个,一个原因是使我自己熟悉实时环境中的服务器/客户端请求(与之类似的典型Web浏览器(不是实时的)之类的东西),并且实际上是在弄湿我的手,因此我可以继续进行正确的设计。
任何人,我都是用C ++编写的,而且我一直在使用winsock来处理基本的基本网络测试。我显然想使用framelimiter并进行3D处理,并且所有这些都在某个时候进行,而我的主要问题是当我执行send()或者receive()时,程序会在那里空闲并等待响应。即使是最好的互联网连接,也可能导致8 fps。
因此,对我来说,显而易见的解决方案是将网络代码从主进程中删除,然后在其自己的线程中启动它。理想情况下,我将在主进程中调用"发送",该进程将向网络线程传递一个指向消息的指针,然后定期(每帧)检查网络线程是否已收到回复,超时或者发生了什么。你。在一个理想的世界中,实际上我会同时运行2个或者多个网络线程,因此我可以说运行一个聊天窗口并进行一件盔甲的背景下载,并且仍然允许播放器一次运行。
我的大部分问题是,这对我来说是新事物。我了解线程的概念,但是我可以看到一些严重的问题,例如如果两个线程试图同时读取/写入相同的内存地址会发生什么情况,等等。我知道已经有处理此类问题的方法了。问题,所以我正在寻找实现此类最佳方法的建议。基本上,我需要线程A能够通过发送数据块,轮询线程B的状态,然后接收答复(也作为数据块)来启动线程B中的进程。理想情况下,不会发生任何重大崩溃。 ^ _ ^我会担心数据实际包含的内容以及以后如何处理丢弃的数据包等,我只需要首先确保这种情况发生即可。
感谢帮助/建议。
PS:只是考虑到这一点,可能会使问题更简单。有没有一种可以利用Windows事件处理系统的优势?像这样,是否有可能让线程A在某个地方初始化数据,然后在线程B中触发一个事件以使其获取数据,反之亦然,线程B告诉线程A完成了吗?那可能会解决我的很多问题,因为我真的不需要两个线程都能够同时处理数据,实际上需要更多的接力棒传递。我只是不知道在两个不同的线程之间是否有可能。 (我知道一个线程可以为事件处理程序创建自己的消息。)
解决方案
我建议调查非阻塞套接字以进行快速修复。使用非阻塞套接字,send()和recv()不会阻塞,并且使用select()函数,我们可以每帧获取任何等待数据。
将其视为生产者-消费者问题:在接收时,网络通信线程是生产者,而UI线程是消费者。发送时,情况正好相反。实现一个简单的缓冲区类,该缓冲区类为我们提供类似push和pop的方法(pop应该在网络线程中阻塞而在UI线程中非阻塞)。
与其使用Windows事件系统,不如使用更可移植的东西,例如Boost条件变量。
我不编写游戏代码,但是我使用了类似于pukku建议的系统。如果需要的话,它非常适合执行诸如使缓冲区优先处理要处理的消息之类的事情。
我认为它们是每个线程的邮箱。我们要发送数据包吗?让ProcessThread创建一个带有有效负载的"线程消息"以继续传输并将其"发送"到NetworkThread(即,将其推送到NetworkThread的队列/邮箱中并发出信号,通知NetworkThread的条件变量,以便他醒来并把它拉下来)。当NetworkThread收到响应时,将其打包为线程消息,然后以相同的方式将其发送回ProcessThread。区别在于,ProcessThread将不会在条件变量上被阻止,仅当我们要检查响应时才对mailbox.empty()进行轮询。
我们可能希望直接推送和弹出,但是对于大型项目,更方便的方法是实现toThreadName,ThreadMsg基类中的fromThreadName方案和线程向其注册其邮箱的邮局。邮局然后有一个send(ThreadMsg *);该功能可基于to和from将消息获取/推送到适当的邮箱。邮箱(缓冲区/队列类)包含ThreadMsg * = receiveMessage(),基本上将其从基础队列中弹出。
根据需要,我们可以让ThreadMsg包含一个虚拟函数process(..),该函数可以在派生类中被相应地覆盖,或者只具有一个普通的ThreadMessage类,该类具有一个to,from成员和一个getPayload()函数来获取该函数。原始数据并直接在ProcessThread中进行处理。
希望这可以帮助。
BlodBath关于非阻塞套接字的建议可能是正确的方法。
如果我们尝试避免使用多线程方法,则可以研究在套接字上设置重叠I / O的用法。当我们进行发送或者接收时,它们不会阻塞,但具有额外的好处,使我们可以选择在单个事件循环中等待多个事件。传输完成后,我们将收到一个事件。 (有关详细信息,请参见此内容)
这与多线程方法不兼容,因此以后可以改变主意。 ;-)
在多线程应用程序的设计上。最好的办法是制定出所有要提醒外部活动。例如,到目前为止,我们在问题中已经列出了网络传输,网络接收和用户活动。
根据要处理的并发连接数,我们可能会发现在每个套接字上都有一个线程在概念上更简单(假设套接字数量很少),其中每个线程负责该套接字的所有处理。
然后,我们可以按照RC的建议在线程之间实现某种形式的消息传递系统。
安排系统,以便在将消息发送到特定线程并发送事件时也进行发送。然后,线程可以被送入睡眠状态,以等待这些事件之一。 (以及套接字事件,用户事件等其他任何刺激因素)
我们说得很对,我们需要小心多个线程尝试访问同一块内存的情况。互斥量和信号量是在那里使用的东西。
另外,请注意GUI在多线程方面的局限性。
关于这个问题的一些讨论可以在这个问题中找到。
但是缩写形式是大多数(并且Windows就是其中之一)GUI不允许多个线程同时执行GUI操作。要解决此问题,我们可以通过向gui线程发送自定义消息以使其执行gui操作,来在应用程序中使用消息泵。
最简单的事情
我们要做的就是简单地调用Windows API QueueUserWorkItem。我们只需指定线程将执行的功能以及传递给它的输入即可。系统会自动为我们创建一个线程池,并在其中执行作业。将在需要时创建新线程。
http://msdn.microsoft.com/zh-CN/library/ms684957(VS.85).aspx
更多控制
我们可以使用另一套API进行更详细的控制,这些API可以再次为我们管理线程池-
http://msdn.microsoft.com/zh-CN/library/ms686980(VS.85).aspx
自己做
如果要控制线程创建和池管理的各个方面,则必须自己创建线程,确定它们应如何结束,创建多少线程等(beginthreadex是用于创建线程的api。使用MFC,则应使用AfxBeginThread函数)。
将作业发送到工作线程Io完成端口
在这种情况下,我们还必须担心如何传达工作,我建议使用IoCOmpletionPorts来做到这一点。我目前知道这是最可扩展的通知机制。它的另一个优点是在内核中实现,因此避免了如果我们决定手动滚动某些内容时可能遇到的各种死角警惕现象。
本文将向我们展示如何使用代码示例-
http://blogs.msdn.com/larryosterman/archive/2004/03/29/101329.aspx
传回Windows讯息
我们可以使用Windows消息将状态传达回父线程,因为它无论如何都在等待消息。使用PostMessage函数来执行此操作。 (并检查错误)
ps:我们还可以在专用指针上分配需要发送的数据,然后工作线程可以在将数据发送出去之后负责删除它。这样,我们也避免了返回指针通信。
我们可能感兴趣的一些主题:
- 互斥锁:互斥锁仅允许我们锁定对一个线程的特定资源的访问
- 信号量:一种确定某种资源仍然拥有多少用户的方法(=有多少线程正在访问它)以及一种线程访问资源的方法。互斥锁是信号量的一种特殊情况。
- 关键部分:一段互斥保护的代码(只有一个车道的街道),一次只能由一个线程传播。
- 消息队列:一种在集中式队列中分发消息的方法
- 进程间通信(IPC)-一种通过命名管道,共享内存和许多其他方式相互通信的线程和进程的方式(这是一种概念,而不是一种特殊的技术)
所有粗体主题都可以在搜索引擎上轻松查找。