after — 일정 시간 뒤에 명령 실행하기

after — 일정 시간 뒤에 명령 실행하기

Tcl에서 "몇 밀리초 뒤에 이 일을 처리해 줘" 같은 요청을 할 때 쓰는 명령이 바로 after예요. 크게 두 가지 쓰임이 있는데, 하나는 프로그램을 그냥 잠깐 멈추는 용도이고, 다른 하나는 미래의 특정 시점에 특정 명령이 실행되도록 예약해 두는 용도죠. GUI 프로그램에서 이벤트 처리 중에 다른 일을 병행할 수 있게 해 주는 게 핵심이에요.

출처: after manual page

본문

after는 첫 번째 인자에 따라 여러 형태로 동작합니다.

after ms

ms는 밀리초 단위의 정수예요. 음수는 0으로 취급합니다. 이 명령은 ms 밀리초 동안 잠들었다가 돌아와요. 잠든 동안에는 애플리케이션이 이벤트에 응답하지 않는다는 점을 알아 두세요.

after ms ?script script script ...?

이 형태는 명령이 즉시 리턴하고, ms 밀리초 뒤에 Tcl 명령이 이벤트 핸들러로 실행되도록 예약해요. 예약된 명령은 그 시점에 딱 한 번 실행됩니다. 지연된 명령은 모든 script 인자를 concat 명령과 같은 방식으로 이어붙여 만듭니다. 예약된 명령은 전역 레벨(어떤 Tcl 프로시저의 문맥 밖)에서 실행돼요. 지연된 명령 실행 중 오류가 나면 interp bgerror로 등록된 명령이 백그라운드 오류를 처리합니다. after는 나중에 after cancel로 취소할 수 있게 해 주는 식별자를 리턴해요. ms가 0(또는 음수)이면 다른 이벤트 유형보다 우선해서 이벤트를 즉시 큐에 넣습니다(이벤트 프로시저 안에 설치된 게 아니라면, 그 경우엔 다음 이벤트 라운드를 기다려요).

after cancel id

이전에 예약해 둔 지연 명령의 실행을 취소합니다. id는 취소할 명령을 가리키며, 반드시 이전 after 명령의 리턴값이어야 해요. id가 가리키는 명령이 이미 실행됐다면 after cancel은 아무 효과가 없습니다.

after cancel script script ...

이 형태도 지연 명령의 실행을 취소해요. script 인자들을 공백 구분자로 이어붙입니다(concat 명령과 동일). 그 문자열과 일치하는 대기 중인 명령이 있으면 취소되어 절대 실행되지 않아요. 일치하는 대기 명령이 없다면 아무 효과가 없습니다.

after idle script ?script script ...?

script 인자들을 공백으로 이어붙이고, 결과 스크립트를 idle 콜백으로 나중에 평가하도록 예약합니다. 이 스크립트는 이벤트 루프에 들어갔을 때 처리할 이벤트가 하나도 없는 다음 순간에 정확히 한 번 실행돼요. after cancel로 취소할 수 있게 식별자를 리턴합니다. 실행 중 오류가 나면 interp bgerror로 등록된 명령이 처리합니다.

after info ?id?

기존 이벤트 핸들러에 대한 정보를 돌려줍니다. id 인자가 없으면 이 인터프리터에서 after 명령이 만든 모든 이벤트 핸들러의 식별자 목록을 리턴해요. id를 주면 그 핸들러를 지정하는데, 이전 after 호출의 리턴값이어야 하고 아직 트리거되지 않았거나 취소되지 않았어야 해요. 이 경우 두 요소짜리 리스트를 리턴합니다. 첫 번째 요소는 id에 연결된 스크립트, 두 번째 요소는 그 핸들러가 idle인지 timer인지를 나타내요.

after *ms*after idle 형태는 애플리케이션이 이벤트 기반으로 동작한다고 가정합니다. 지연된 명령들은 애플리케이션이 이벤트 루프에 들어가지 않으면 실행되지 않아요. tclsh처럼 평소 이벤트 기반이 아닌 애플리케이션에서는 vwaitupdate 명령으로 이벤트 루프에 들어갈 수 있습니다.

예시

Tcl이 N초 동안 아무것도 하지 않고 대기하도록 하는 명령을 정의해 볼게요.

proc sleep {N} {
    after [expr {int($N * 1000)}]
}

8시간 뒤에 wake_up 명령이 실행되도록 예약합니다(그 시점에 이벤트 루프가 활성화돼 있다면):

after [expr {1000 * 60 * 60 * 8}] wake_up

다음 명령은 오래 걸리는 계산을 단계별로 처리하는 기법이에요. ::my_calc::one_step은 한 단계를 더 수행해야 하는지 불리언을 리턴한다고 가정합니다. 계산 자체가 단계적으로 동작하도록 구성돼 있어야 하고, 이벤트 큐가 비워졌을 때만 이미 트리거된 타이머 이벤트로 다음 단계를 예약해서 이벤트 루프가 굶기지 않도록 신중하게 만든 겁니다. 느린 작업 중에도 Tk GUI가 응답성을 유지하길 원할 때 유용해요.

proc doOneStep {} {
    if {[::my_calc::one_step]} {
        after idle [list after 0 doOneStep]
    }
}
doOneStep

더 알아보기

  • concat, interp, update, vwait — 인자 이어붙이기, 백그라운드 오류 처리, 이벤트 루프 진입
  • 키워드: cancel, delay, idle callback, sleep, time