DAY 10:Two-phase Termination Pattern,我就跟你说不要乱拔电源!

什麽是 Two-phase Termination Pattern?

分两个阶段关闭 goroutine,第一阶段先结束 goroutine 的程序逻辑,第二阶段再结束 goroutine 本身

此模式的核心目标如以下三点:

  1. 安全的终止 goroutine(安全性)
  2. 必定会关闭 goroutine(生命性)
  3. 发出关闭请求後尽快运行逻辑关闭的处理(响应性)

以 golang 来说我们关闭 goroutine 会采取关闭 channel 的方法:

package main

func main() {
	done := make(chan bool)
	go func() {
		for {
			select {
			case <-done:
				// 关闭的善後程序
				return
            // 其他 case
			}
		}
	}()

	close(done)
}

这样的关闭方式其实就有满足以上三点了。收到done channel被关闭的讯号後,goroutine 会执行case <-done:区域的善後程序并 return 关闭 goroutine:

  • 达到安全性:不会有意外退出的情形。
  • 达到生命性:不会有 goroutine 是暂停状态的情形而无法关闭。
  • 达到响应性:跟生命性的问题相关,不会有 goroutine 是暂停状态的情形而没有处理关闭程序

所以在平常使用 golang 的时候并不会思考这些问题,Two-phase Termination Pattern 主要是在 java 这类「直接控制 thread 生命周期」的语言才会应用到。

java 的 thread 是用物件封装的,开发者可以控制 thread 的生命周期,并且提供了 Thread.stop()来关闭 thread,但这是不安全的,他会使运行的 thread 突然中止,就好像正在计算的电脑,插座直接被拔掉了一样。

因此 java 开发者不建议用Thread.stop()关闭 thread,而是用Thread.interrupt()加上flag的方式,

  • Thread.interrupt()是为了关闭正处於Thread.sleep()Thread.wait()这类「暂停状态」的 thread
  • flag是为了关闭正在运行的 thread

如下:

try {
    while (isRunnable) {
        doSomething();
    }
} catch (InterruptedException e) {
} finally {
    doShutdown();
}

如果 thread 正在运行,while (isRunnable)这个flagfalse的话就可以控制是否运行。

如果 thread 运行Thread.sleep()Thread.wait()导致 thread 暂停,Thread.interrupt()就可以使 thread 抛出异常InterruptedException,并执行最後的doShutdown()善後程序。

java 设计会以一个 function 执行Thread.interrupt()与改变flag,而正在运行的 thread 再执行关闭动作,

public void terminate() {
	isRunnable = false;
	interrupt();
}

Thread.stop()Thread.interrupt(),会较能比对出 Two-phase Termination Pattern 「两阶段终止」的行为。

而 golang 不能直接控制 goroutine 的生命周期,无法做出 java 直接关闭 thread 的行为,所以原本 channel 的做法,即符合 Two-phase Termination Pattern 的目标。

问题情境

设计一个提示,提示关闭程序後显示。

解决方式

相关的 code 在Github - go-design-patterns

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	done := make(chan os.Signal, 1)
	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for {
			select {
			case <-done:
				fmt.Println("bye bye")
				os.Exit(1)
			}
		}
	}()

	// do something

	select {}
}

done{} channel 会透过signal.Notify()os.Interruptsyscall.SIGINTsyscall.SIGTERM这些关闭相关的 system 讯号绑定,并以 goroutine 的方式一直运作在整个程序。

再执行do something的部分时,只要我们使用control+c或者其他关闭程序的方法,channel 接收到关闭讯号就会安全地执行case <-done:范围的 code,即在 terminal 显示bye bye提示并关闭程序。


<<:  [第九天]从0开始的UnityAR手机游戏开发-介绍Unity写程序的基本语法。

>>:  Day 23 何谓黑帽、白帽 SEO?

Day 2 基本工具及套件

前言 今天我会说明一些开始实作所需要的工具,包含 flask 套件。但单单一个 flask 并不足以...

Day 30 - 游艇网页专案完成後的优化方向 - ASP.NET Web Forms C#

=x= 🌵 专案优化方向说明。 网站後台优化 : 1. Sign In Page : 验证密码时间较...

Day3:进入新手村前先让我复习一下QQ-CSS-box-sizing

box-sizing这个语法可以让设计师或是写程序的人不用去加加减减的计算区块 (比如说线条宽度或是...

Day29影片教学:Azure小白想早下班--之--使用Azure Synapse Analytics汇入数PB资料

在昨天我们讲过Azure小白如何使用Azure Cache for Redis来存取常用资料後 今天...

Day19 网页的页首header

今天我们就要着手开始实作拉!而造顺序来的话我们最上方都会有个logo跟导览列,接下来就让我们把学过的...