rubyで細かい単位でwaitを入れる

 なかださんにコメントで教えてもらったのですが、rubyのsleepは0.5などと指定することが出来るので、以下のような物を作る必要はありません。
 また、Cのソースの方もbusy loopな作りになっていてよろしくありません。usecの単位で止める場合、usleep()やnanosleep()などの関数で作れるので、そちらを使うべきだと思います。


 さらになかださんにコメント欄で教えていただいたので追加しておきます。コメント欄を見てもらえば良いと思いますが一応…。
 一般的なCなどのソースコードの場合は上記方法で問題ありませんが、rubyの場合は他のスレッドが止まってしまうので、rb_thread_wait_for()という関数を使った方がよいでしょう。
 rb_thread_wait_for()についてはrubyソースコードsvn.ruby-lang.orgを見るとどういう物か分かります。
 また、rubyのスレッドについて良くしりませんでしたので、Rubyソースコード完全解説 第19章 スレッドを参考にしました。Rubyソースコード完全解説のページはrubyソースコードを読む上で参考にさせてもらっています。

 この文章の意味が分からなくなると思うので、ソースなどはそのまま残しておきます。


 作業をする際にちょっとしたスクリプトを以前はperlで書いていたけれど、最近はもっぱらrubyで書いてます。で、今回msecやusecの単位で待ち時間を入れる必要があって、ちょっと書いてみたのでコピペしてメモ。


extconf.rb :

#! /usr/bin/ruby

require "mkmf"

create_makefile("waittime")

waittime.c :

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#include "ruby.h"

static double time_mark(void)
{
    struct timeval tv;
    struct timezone tz;

    gettimeofday(&tv, &tz);

    return((double )tv.tv_sec + 1E-6 * (double )tv.tv_usec);
}

static VALUE 
ruby_idle(self, waittime)
    VALUE self;
    VALUE waittime;
{
    double start;
    double now;

    start = time_mark();
    do {
        now = time_mark();
    } while((now-start) < NUM2DBL(waittime));

    return Qnil;
}		  

void Init_waittime()
{
    VALUE cWaitTime;

    cWaitTime = rb_define_class("WaitTime", rb_cObject);
    rb_define_method(cWaitTime, "idle", ruby_idle, 1);
}

使い方

  • extconf.rb, waittime.cを作成
  • extconf.rbを実行してMakefileを作成
  • makeを行う
  • irbなどでrequire "waittime"してwait=WaitTime.newしてwait.idle(500e-3)で500msec待つ

 うむ、簡単だ。
 作って気がついたけれど、Timeオブジェクトを使えば同じようなことがrubyだけで出来るので、Cのライブラリを作るまでも無かったかもしれん。
 …Cのライブラリを作る手順のメモということで。ライブラリを作成する上での詳細はsvn.ruby-lang.orgを読むとか。