2018年11月10日土曜日

[MQL4] OnTick() と OnTimer() は同一スレッドで動いている

@MetaTrader 4.00 Build 1090

Expert Adviser の `OnTick()` で何か重たい処理をしているとき本来は、なるべく重い処理はしない方が良いけれど、`OnTimer()` はどのように処理されるのか、調べてみました。

結論から言うと、以下のような動作をするようです。
(同一スレッド上で動いているか、少なくとも排他されている。)

- `OnTick()` の処理中に `OnTimer()` のタイミングが来た場合、`OnTick()` 終了直後に`OnTimer()`が実行される(`OnTimer()` はペンディングされる)
- ただし、2回以上ペンディングされたとしても、`OnTick()` 終了直後に実行される `OnTimer()` は1回のみ
- ペンディングされたとしても、`OnTimer()` が呼ばれるタイミングには影響しない(`EventSetTimer()` が呼ばれた時点から指定間隔で呼ばれ続けるただし、多少の誤差はある)

この動作、逆の場合も当てはまるようです。
つまり、`OnTimer()` で重たい処理をしていると、その間に呼ばれるはずだった `OnTick()` が無視されることになります最後の一回を除いて。

また、`OnTick()` で重たい処理をしていても、同じように `OnTick()` が無視されます。

`OnTick()` や `OnTimer()` ではなるべく重たい処理をしないようにすべきですね。
かつ `OnTimer()` の呼び出し頻度は少なめに。



### 確認環境
以下のような EA を作成し、タイミングをいろいろ変えて確認してみました。

```mq4
`title: "Experts/ThreadTest.mq4";
#property strict

static uint count = 0;

int OnInit()
{
    //--- create timer
    EventSetTimer(1); // 1秒(本来こんなに高頻度で呼ぶべきではない)

    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    //--- destroy timer
    EventKillTimer();
}

void OnTick()
{
    count++;
    printf("OnTick %u start", count);
    Sleep(3000); // 3秒待つ
    printf("OnTick %u end", count);
}

void OnTimer()
{
    Print("OnTimer");
}
```