TinyLab

Keep eyes on the star and feet on the ground.


  • 首页

  • 归档

  • 分类

  • 标签

UE并发-Async & Future & Promise

发表于 2021-08-14   |   分类于 UE从里到外

Simplicity is the ultimate form of sophistication ! - Leonardo Da Vinci

UnrealEngine有了跨平台的线程、线程池和TaskGraph,再来看看Async/ParalleFor和Future/Promise的实现,由衷的感受下上面的话:简约是复杂的最终形式。这些操作实现都不复杂,却有着强大的能力,让异步并发代码写起来简洁易懂。下面简单介绍下:

  • Future & Promise的实现和简单用法
  • Async系列接口的用法
  • Paralle接口的用法

另外这里相关接口可以类比C++标准库中的概念。UE中这些接口是基于之前提到的几种并发机制实现的,废话不多说,上代码,看注释。

1.Future & Promise

1.1.实现

Future和Promise的概念,和C++标准中的std::future/std::promise类似:

  • Future - 一个会在未来某个点返回的值
  • Promise - 一个异步函数,在某个并发执行体中设置,然后Future变得有效

实现比较简单,类图如下:

Future

  • TPromise<ResultType> - Promise
  • TFuture<ResultType> - Future
  • TFutureState<ResultType> - Future和Promise共享的状态。
    • Promise创建时创建一个State
    • GetFuture()时返回一个共享该状态的Future对象
    • 基于FEvent实现,等待和触发
阅读全文 »

UE并发-TaskGraph的实现和用法

发表于 2021-08-11   |   分类于 UE从里到外

TaskGraph是UE中基于任务的并发机制。可以创建任务在指定类型的线程中执行,同时提供了等待机制,其强大之处在于可以调度一系列有依赖关系的任务,这些任务组成了一个有向无环的任务网络(DAG),并且任务的执行可以分布在不同的线程中。

ThreadPool

TaskGraph支持两种类型的线程:

  • 一种是由TaskGraph系统后台创建的线程,称之为AnyThread。
  • 另一种是外部线程,包括系统线程(比如:主线程)或者其他基于FRunnableThread创建的线程,初始化的时候需要Attach到TaskGraph系统,此类线程成为NamedThread。
阅读全文 »

UE并发-线程池和AsyncTask

发表于 2021-08-05   |   分类于 UE从里到外

为了更高效地利用线程,而不是每个任务都创建一个线程,UE中提供了线程池的方案,可以将多个任务分配在N个线程中执行。任务过多时,排队执行,也可以撤销排队。本文简单介绍下:

  • 线程池FQueuedThreadPool的实现。
  • 使用IQueuedWork自定义一个简单任务类,并放入线程池中执行。
  • 引擎全局的几个线程池:GThreadPool、GIOThreadPool、GBackgroundPriortyThreadPool、GLargeThreadPool。
  • AyncTask的用法。

1.线程池的实现

结构:

ThreadPool

接口层:

  • IQueuedWork - 任务接口,继承使用。
  • FQueuedThreadPool - 线程池的接口类,常用操作:
    • AddQueuedWork - 把任务放入线程池中执行,若有空闲线程,直接分配给空闲线程,若没有空闲线程,放入线程池维护的队列,后台线程会从队列中自己拿任务执行。
    • RetractQueuedWork - 撤回指定任务,只能撤回正在排队的,已经在执行的没法撤回。

实现层:

  • FQueuedThreadPoolBase - 线程池的实现类

    • QueueWork - 排队的任务
    • QueuedThreads - 空闲的线程
    • AllThreads - 所有线程(FQueueThread)
  • FQueuedThread - 线程池的后台线程实现

    • 线程运行时,若没有任务则挂起,有任务时执行任务,执行完一个任务后,从线程池队列中再拿一个执行(FQueuedThreadPoolBase::ReturnToPoolOrGetNextJob),直到没有任务,再次挂起自己
    • 若目前线程为空闲,放入一个任务后,执行该线程的DoWork,结束挂起开始执行任务

运行示意图:

ThreadPool

阅读全文 »

UE并发-生产者消费者模式

发表于 2021-08-03   |   分类于 UE从里到外

0.Overview

生产者消费者模型,在并发编程中经常会用到,Unreal Engine中也封装了相应的无锁数据结构:

  • TQueue - 一个无锁的不限制大小的队列,支持SPSC(单生产者单消费者)/MPSC(多生产者单消费者)两种模式。
  • TCircularQueue - 一个无锁环形队列,SPSC(单生产者单消费者)模式下线程安全。
  • TTripleBuffer - 一个无锁的三缓存实现,SPSC(单生产者单消费者)模式下线程安全(UE源码中没有用到,所以本文不涉及其用法,不过其思路在物理模块中有用到)。

另外,针对MPMC/SPMC(多消费者)模式,需要自行实现,示例代码中做了简单实现,供参考。本文主要包含:

  • 生产者消费者模型及其模式
  • UE中TQueue和TCircularQueue的实现和用法
  • 一个双缓冲的简单实现,双缓冲主要用来优化队列读写锁定的开销,在服务器中一般用于IO线程和逻辑线程之间做数据交换。
  • 一个MPMC模式的简实现,用到锁和事件。
阅读全文 »

UE并发-Lockfree编程及其在TaskGraph中的应用

发表于 2021-07-30   |   分类于 UE从里到外

在多线程程序中为了保证数据一致性,一般会使用同步机制,比如,使用临界区或Mutex来加锁获取资源,解锁释放资源。用锁不当,会产生死锁(Deadlock)、活锁(Livelock)、饥饿(Starving)等问题,另外在某些对性能要求苛刻的地方,也不是最佳选择。

若把同步机制叫软件锁(Software Lock),那么原子操作可以认为是硬件锁(Hardware Lock)。而基于原子操作实现的无锁数据结构和算法,在这些需要考虑加锁/解锁效率的情况下,值得一用。当然无锁数据结构要精心设计,任何一行代码都要慎重考虑,不光是字面代码,还得考虑编译器优化,内存模型,甚至CPU架构。看一看C++11标准引入的atomic就知道有多复杂了,为了性能也是拼了。

UE中支持了无锁队列(TLockFreePointerListFIFO)和无锁堆栈(TLockFreePointerListFIFO),在TaskGraph中的任务队列就是基于此实现的,作为任务多线程调度的核心模块,性能肯定是至关重要的。更加复杂的LockFree数据结构实现起来非常麻烦,正确性也很难保证。下面主要介绍下:

  • UE中的无锁队列的用法,超级简单。
  • 无锁编程的关键技术点,若要自己设计一个无锁队列,要考虑哪些技术点?CAS原子操作、ABA问题、内存屏障等。
  • UE中无锁队列的实现,用起来简单的背后,就没那么简单了。
  • TaskGraph中无锁队列的使用。
阅读全文 »

UE并发-线程和同步机制

发表于 2021-07-26   |   分类于 UE从里到外

Unreal Engine提供了多种并发机制,从简单的原子操作,到复杂的TaskGraph系统。线程及其同步机制是最基础的,其本身许多内容和C++标准线程库、Pthread等线程库并无二致。本文简单整理下,UE并发中最基础的内容:

  • 线程/线程管理器的结构
  • 线程的常见操作
  • 三种同步机制:原子操作、临界区/读写锁、事件机制。
阅读全文 »

使用UnrealEngine创建独立应用

发表于 2021-07-19   |   分类于 UE实践笔记

0.动机

UE源码简直就一个宝库,里面封装了许多有用、好玩的代码。想一探究竟,还是得动手,尤其是低层代码,写点测试代码验证下和自己的理解是否有偏差也很重要。能脱离UEEditor,快速写点独立的测试代码?能把UE源码当作一个可复用的快平台的代码库来用?能用UE写独立的GUI工具?

答案当然都是:YES!下面尝试解决这几个问题:

  • 快速编写测试代码
  • 创建独立应用程序(比如:命令行工具、基于Slate界面的工具)
  • 把UE当作一个代码库来用,开发自己的各种应用

测试代码仓库(不定期更新):https://github.com/david-pp/UESnippets

阅读全文 »

MMO从里到外

发表于 2020-09-02   |   分类于 游戏开发

献给那些创造世界的人们(To those who would make worlds)。

作者Richard A. Bartle,相信做过MMO的同学都有所耳闻Bartle提出的玩家分类模型:

MMO

在MUD/MMORPG世界里面,从玩家和世界互动的两个维度去看,分为四类:

  • 1、杀手(Killers):各种PK玩法,帮会群战,国战,竞技场。
  • 2、社交(Socializers):情缘,结拜,帮派。
  • 3、成就(Achivers):排行榜,国王,盟主,数值成长。
  • 4、探索(Explorers):剧情,探索。

Killers & Socializers都是喜欢和人互动的,一个乐于竞争,一个乐于社交。Achivers & Explores都是喜欢独自和游戏设计者开发的世界进行互动,一个享受探索的乐趣,一个享受游戏世界带来的成就感。有道是,以史明智,作者的这本《MMOs From The Inside Out》谈论MMO的历史发展、MMO的设计、MMO的乐趣所在、MMO的艺术性、我想作为一个MMO的从业者,无论你是策划还是技术,都能从中了解到许多,成为一个创造MMO世界的设计者。

MMO

关于历史

Bartle从1978到2015(本书出版时间),把MMO的发展分为6个世代,同时描述了MMO里面很多关键概念的来源,比如:World、PK、Tank、DPS、PvP、PvE等等耳熟能详的词语,列举了大量听过/没听过的游戏,从只有文字的MUD、2D Titled-Based、2.5D、直到全3D(第一人称、第二人称、第三人称)。

历史的车轮从未停止过,那么MMO的未来又会怎样?拭目以待:更高质量的画面表现?更多开放世界式探索式的玩法?和游戏世界本身更多的交互?FPS MMO?生存建造类MMO?或许,终极MMO,就像《头号玩家》那样。作为一个技术人,面对未来,我们也会遇到非常多的技术挑战,无论是服务器和客户端引擎,MMO缩写中第一个M非常难搞(Massive)。

关于设计

设计能力是游戏行当每个工种都必须具备的能力。程序要懂程序设计、策划要懂游戏性/系统设计、美术关注艺术性上的设计。Design需要具严格的理性逻辑和火热的激情,在好奇心驱动,不留余力地拆解分析,化整为零,打碎重建,进行系统化思考,不断地原型迭代,具备一定的工程思维等。Bartle关于设计,谈了很多,有关于Designer的,有关于GamePlay的,甚至有关于程序语言选择,数据库,数据流,单继承,多继承等。随便列举几条:

  • A Wise Designer:明智的设计者应该善于倾听玩家,不明智者则拒绝倾听,甚至听到也置若罔闻。
  • 60 Seconds of GamePlay:若一个玩法60秒无法描述清楚,说明你并没有彻底了解它。
  • Player wants:玩家想要的分为三个层次,他们知道自己想要的是什么,设计者知道他们真正想要什么,直到摆在他们面前他们才知道这是想要的。
  • Balance:时刻关注玩家和玩家之间的平衡,玩家和Gameplay的平衡,Gameplay和Gameplay之间平衡。

关于乐趣

MMO的乐趣何来?此章节太多内容,关于沉浸感、Hacker Culture、游戏作为探索的乐趣、游戏将故事的乐趣等等。同时也谈到,什么是不好玩:太少限制或者太多限制。

关于艺术

游戏号称第九艺术,艺术给玩家最直接的感官体验。游戏的玩法也充满艺术性设计,比如:战争的艺术,无尽的艺术等等。游戏开发的过程也充满艺术性,比如:说服的艺术。

MMO

最后,限于时间,书中内容只能简单列举。这本书涉及的内容有点广,真的是“从里到外”,有点像意识流的风格。不过都是作者关于MMO和游戏的一些真知灼见,有时间针对某些点拿来反复把玩,深入思考设计问题,归纳总结,激发灵感,还是不错的。

游戏,作为理性与激情的产物。特别喜欢Bartle结尾的话:

如果你认为游戏是一个工具,那么本书就不用写了,只剩下一句话:复制一款已有的游戏,重新设定它(Copy an existing game and repurpose it)。如果你认为它是一种艺术,那么你的倾注一点自己的灵魂在里面(you have to put some of your SOUL into it)。

PS. 内容太多,没来得及细看,感兴趣的同学可以自行翻阅。若需要电子版,可以公众号私聊,回复:“MMO”。

拥抱开源的正确姿势

发表于 2020-03-08

我们都知道,尽量不要重复造轮子,遵守DRY(Don’t Repeat Yourself)原则。遇到问题,先去找找前人是否已经有比较好的方案,大多问题,开源世界可能已经提供了相应的工具和代码,我们要做的事情就是学习这些开源项目,然后应用到自己的项目。那么问题来了:

  • 解决同样问题的开源项目,可能会有很多,各有千秋,怎么选?
  • 怎么快速学习和使用一个开源项目?
  • 开源项目要学习到什么程度,应用时才能高枕无忧?
阅读全文 »

游戏开发中的算法

发表于 2020-03-01

游戏技术这条路,可深可浅。你可以满足于完成GamePlay玩法层面的东西,你也可以满足于架构和框架设计层面的东西,你也可以醉心于了解某一游戏引擎带来的掌控感。但是,我们不该止步于此,止步与目前所见或所用,这里有一条路,无关乎游戏玩法,跨越引擎和游戏服务器架构,是一条超级硬核的路,尝试去了解比引擎或服务器框架更底层,更通用的东西,由于涉及到大量数学公式、物理知识和算法,并且跨越计算机多个领域,所以会异常艰难,需要翻跃千山万水,才能一探究竟。

结构是技术的骨骼,那么算法就是技术的灵魂。下面只能简单列举在游戏开发的几个基础领域,算法要求比较高的地方:图形学、AI、物理、分布式计算及其需要的数学知识。另外,游戏玩法层面的,比如场景管理、各种子系统也会涉及到算法,不在此列。由于内容众多,只能简单列举算法所在子领域,具体详情可去翻阅推荐的参考资料。

阅读全文 »
12…13
David++

David++

123 日志
25 分类
65 标签
RSS
GitHub 知乎 微博 豆瓣
© 2007 - 2021 David++
由 Hexo 强力驱动
主题 - NexT.Pisces