- PR -

スレッドタイマにて一定間隔で呼び出すメソッド内のDLL関数が実行されない

投稿者投稿内容
おいたん
会議室デビュー日: 2008/03/10
投稿数: 15
投稿日時: 2008-06-03 19:05
VC#2005で開発しています。

フォームのボタンを押すと、処理が実行されるとともに
スレッドにてステータスを監視する、というアプリを実装しています。

スレッド内のステータス監視は、処理が実行されているあいだに
10ms程度の間隔で、繰り返し行いたいと考えてます。

そこで、ボタン押下時にスレッドタイマを起動させ、処理完了
後にdisposeすることとしました。
(timerCallBackをdelegateとして、timerをnewするときに左記の
delegateと、開始までの時間、繰り返し間隔等を引数にしてます)
http://www.atmarkit.co.jp/fdotnet/dotnettips/373threadtimer/threadtimer.html

ところが、delegateで渡したメソッド内のステータス監視関数が
実行されないようなのです。ブレークポイントを置いてみますと、
同関数の直前までは処理が問題なく進んでいます。しかし、同関数は
実行されず、もちろん戻値は返しませんし、try-catchでの
例外処理にもかかりません。
 
同関数は、別クラス内で定義されていて、DLLインポート
されたものです。

同関数をコメントアウトすると、残りのメソッド内処理は行われます。

上記について、どのあたりに問題があるのでしょうか。ご教授願います。
※記載に不足がありましたら指摘ねがいます。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2008-06-03 23:06
バグがあるんでしょうけど、コードがないとさすがに難しいですね。
_________________
囚人のジレンマな日々
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2008-06-03 23:16
引用:

おいたんさんの書き込み (2008-06-03 19:05) より:
ところが、delegateで渡したメソッド内のステータス監視関数が
実行されないようなのです。ブレークポイントを置いてみますと、
同関数の直前までは処理が問題なく進んでいます。しかし、同関数は
実行されず、もちろん戻値は返しませんし、try-catchでの
例外処理にもかかりません。


「同関数は実行され」ていないのは確かでしょうか?戻ってこないことは確認されているとは思いますが、実行されていないことを確認することは難しいのではないでしょうか。
ひとつの推理ですが、その関数がメッセージループを内部に持っていて、関数が実行されて関数の中には入っているけど、メッセージループがメッセージを処理するためそこから抜け出せない状態になっているということはないでしょうか。私は自分ではそういうのは外側でやりたいので、中には持たせませんが、昔から、中に持たせているような API は巷では結構見かけます。

あとは、なにかバグがあって関数を呼ぶとスタックを壊しているなどでしょうか。

「スレッド・タイマ」を使われているそうですが、ためしに「サーバベース・タイマ」を使い、「タイマ・メソッド」がUIスレッドで動くようにしてみてはどうでしょうか。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2008-06-04 00:49
Timerのインスタンスをローカルで作成してそのままメソッドを抜けていて、参照を保存してないとかないですか?

記事のコードも実際には問題あります。
※記事の方はデバッガ上では普通に動いてしまうでしょうけど

System.Threading.Timerの解説のところ
>Timer を使用している間は、このクラスへの参照を保持しておく必要があります。他のマネージ オブジェクトと同様、まったく参照されていない場合、Timer はガベージ コレクションの対象となります。Timer がアクティブであっても、ガベージ コレクションの対象から除外されることはありません。

--追記
とと、失礼、処理完了後にDisposeって書かれてますね…
やっぱりある程度ソースがないとわからなさそうです…

[ メッセージ編集済み 編集者: なちゃ 編集日時 2008-06-04 00:55 ]
おいたん
会議室デビュー日: 2008/03/10
投稿数: 15
投稿日時: 2008-06-04 09:53
皆様、ご回答いただきありがとうございます。
対象部分のコードを以下に示したいと思います。

//-------以下、コード-----------------------------------//
コード:
//-------メインフォーム----------//
public class MainForm : Form
{
    private ProcClass proc = new ProcClass();  

    TimerCallback timerDelegate;
    Sysmte.Threading.Timer timer1;

    private void button1_Click( object sender, EventArgs e )
    {
        timerDelegate = new TimerCallback( proc.timerMethod );
        timer1 = new System.Threading.Timer( timerDelegate, null, 0, 10 );
        proc.hoge();
        timer1.Dispose();
    }
}
///////////////////////////////////

//---------処理内容クラス--------//
class ProcClass
{
    private ushort kore;

    public ProcClass()
    {
        kore = ARE;
    }
    public void hoge()
    {
    DLLimp.dllStart( kore );
    }

    public void timerMethod( object o )
    {
        if( DLLimp.dllCheckStatus( kore ) )
        {
          DLLimp.dllEmgStop( kore );
        }
    }
}
///////////////////////////////////

//-------dllインポートクラス-----//
class DLLimp
{
    [System.Runtime.InteropServices.DllImport("hoge.dll")]
    public extern static bool dllStart( ushort dore );
    [System.Runtime.InteropServices.DllImport("hoge.dll")]
    public extern static bool dllEmgStop( ushort dore );
    [System.Runtime.InteropServices.DllImport("hoge.dll")]
    public extern static bool dllCheckStatus( ushort dore );
}
///////////////////////////////////

//-------以上、コードでした-----------------------------//



上記のような感じです。
ちなみに、スレッドタイマを使わずに、1回だけステータスの監視
をするようなコード(ProcClassのhoge関数内に
dllStart、dllCheckStatusを続けて実行)であれば問題なく動作します。
ので、dllインポートした関数内から処理が抜けられない、といった
問題ではないと考えます。
が、狙いはdllStart実行中に繰り返しdllCheckStatusを行いたい、
ということです。

なちゃ様指摘の「参照を保持しておく」の意味がよくわかっていません。
あわせてご教授願えないでしょうか。

以上、よろしくお願いいたします。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2008-06-04 10:01
そりゃタイマを Dispose したら動いてたタイマは止まるでしょう。
おいたん
会議室デビュー日: 2008/03/10
投稿数: 15
投稿日時: 2008-06-04 10:13
Hongliang様、ご回答いただきありがとうございます。

class ProcClassのhoge関数内で実行している
dllStartは、完了に数秒を要します。ので、その前に
スレッドタイマにてステータスを監視するdllCheckStatus
を繰り返し実行するようにしておき、dllStartが完了
すれば、タイマは不要ということでDisposeしている、
という意図でこのようなコードにしています。
(この意図からして誤っているのでしょうか?)

ちなみに、Disposeをしなかったとしても、dllCheckStatus
は実行されませんで(実行直前までいくことはブレークポイントで
確認しています)、dllStartを実行完了後、フォームに
戻ってしまいます。

以上、よろしくお願いいたします。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2008-06-04 15:16
引用:

おいたんさんの書き込み (2008-06-03 19:05) より:
スレッド内のステータス監視は、処理が実行されているあいだに
10ms程度の間隔で、繰り返し行いたいと考えてます。


引用:

おいたんさんの書き込み (2008-06-04 09:53) より:
timer1 = new System.Threading.Timer( timerDelegate, null, 0, 10 );


目論見が良く分からないのですが、いきなり 10ミリ秒間隔で試さないといけないのでしょうか?最初は10〜100秒間隔くらいで試したほうがデバッグしやすいと思うのですが。

System.Threading.Timer を使ったマルチスレッドは私はあまり良く分からないのですが、タイマー一般の話で言うと、間隔が短いと、「タイマ・メソッド」が呼ばれて、そこから抜けないうちに、あらたに「タイマ・メソッド」が呼ばれ、「タイマ・メソッド」が多重に動いてしまう、ということになる可能性はないですか?

「タイマ・メソッド」の問題ではなく DLL の関数の問題であることは確かなのでしょうか?DLL の関数を使わず、ダミーの .NET の関数を使った場合はちゃんと動くのでしょうか?

スキルアップ/キャリアアップ(JOB@IT)