UPC 提供了障碍,篱笆和锁这三种同步原语来控制线程之间的交互作用。
障碍(barrier)是用来实现线程之间同步的原语。障碍又分为阻挡障碍(blocking barrier)和非阻挡障碍(non-blocking barrier)。
阻挡障碍如图 6 所示,线程 1 和线程 2 以不同的速度在执行程序,当线程 1 遇到阻挡障碍语句 upc_barrier 的时候会停下来等待线程 2。当线程 2 也执行到该阻挡障碍语句 upc_barrier 的时候,线程 1 才会继续向前执行。
图 6. 阻挡障碍示意图
非阻挡障碍由 upc_notify 和 upc_wait 组成。如图 7 所示,线程 1 和线程 2 以不同的速度在执行程序,当线程 1 遇到非阻挡障碍语句 upc_notify 的时候会通知线程 2 它已经执行到 upc_notify 的这个程序点了,然后线程 1 会继续执行,当执行到 upc_wait 才停下来等待线程 2,一直等到线程 2 也执行到该 upc_notify 的程序点发出到达报告,这时候线程 1 才继续向前执行。一般在 upc_notify 和 upc_wait 之间,程序员可以让线程进行无关其他线程的运算,这样充分地利用了线程 1 的等待时间,提高了程序的性能。
图 7. 非阻挡障碍示意图
篱笆(upc_fence)是用来实现线程内的同步的原语。当一个线程执行遇到篱笆的时候,它会确保所有在篱笆之前对共享数据的访问全部完成之后,再执行在篱笆之后对共享数据的访问。
锁(lock) 在多线程的环境中,多个线程同时对一个共享数据的操作,会造成竞争状态。一个线程对一个共享数据的修改可能会使得其他线程读到不正确的数据。锁用来确保在同一的时间内只能有一个线程进行访问某一共享数据。锁是以牺牲程序性能来保证程序的正确性,因为它阻止了多线程的同步执行,所以在保证程序的正确性的基础上,尽量避免锁的使用。请参见使用库函数中的锁