DOBON.NET DOBON.NETプログラミング掲示板過去ログ

排他制御の処理順番

環境/言語:[OS : Windows XP Professional / 言語 : Visual Basic .NET / .NET Framework : 2.0]
分類:[.NET]

【解決したい問題】

実現したい処理の流れ
1.クライアントから印刷要求を受け取る。
2.サーバ側で要求を受け取って、スレッド生成(クライアント側で別処理を行いたいため)。
3.受け取った要求からIPアドレスを抽出して、プリンタへ印刷データを送信。

実現するために以下の方法をとりました。
1.デリゲートによるスレッド処理の呼び出し(BeginInvoke)
2.接続するプリンタのIP毎にミューテックスクラス生成
3.ミューテックスの所有権を要求。
4.所有権を獲得できたら、プリンタへ印刷データを送信
5.ミューテックス解放

これで複数プリンタが存在しても、同時に印刷データを送信し、並行して処理ができたのですが、同じプリンタに要求があった時に、リクエストがあった順番で印刷ができませんでした。
ミューテックスは排他制御をするだけで、処理順番まで制御できないとは思うのですが、これを実現するために何かよい方法はないでしょうか。
> 実現するために以下の方法をとりました。
> 1.デリゲートによるスレッド処理の呼び出し(BeginInvoke)
> 2.接続するプリンタのIP毎にミューテックスクラス生成
> 3.ミューテックスの所有権を要求。

  このミューテックスを生成しているのは、サーバー?クライアント?
  どちらでしょうか。

> 4.所有権を獲得できたら、プリンタへ印刷データを送信
> 5.ミューテックス解放

  共有プリンター(サーバー側)の印刷順序は、FIFO順ではなく、
  受信を終わった順序で動作していると思いますが・・・

  要は、先に受け付けても、印刷データのページ数が多かったりして
  後から受信したJOBが少ないページの場合、先に完了してそちら
  が先に印刷されるので、順序が異なる・・・と申されているように
  見受けられます。

> これで複数プリンタが存在しても、同時に印刷データを送信し、並行して処理ができたのですが、同じプリンタに要求があった時に、リクエストがあった順番で印刷ができませんでした。
> ミューテックスは排他制御をするだけで、処理順番まで制御できないとは思うのですが、これを実現するために何かよい方法はないでしょうか。

  サーバーOSのキューイングとクライアントOSのキューイングに
  機能的差異がありますので・・・

※ 何故、そのようなことをしないといけないのでしょうか?

  どうしても順序を管理したいと言うことであれば、共有プリンター
  に印字指令を出すのではなく、サーバー側に印字データを受け取る
  サービスを立て、そこで順番管理を行えば、一度に接続可能なPC
  は限定できますので、順番は確保できます。

  しかしクライアントPC台数が多い場合、非常に応答性の悪いシス
  テムになってしまいますので・・・

  ほかにもやり方はあると思いますが、こんなところでしょうか・・・

以上。参考まで
2009/07/08(Wed) 12:08:21 編集(投稿者)
2009/07/08(Wed) 12:08:16 編集(投稿者)

■No24907に返信(オショウさんの記事)
オショウ様回答ありがとうございます。

>   このミューテックスを生成しているのは、サーバー?クライアント?
>   どちらでしょうか。

サーバ側です。
>
>   サーバーOSのキューイングとクライアントOSのキューイングに
>   機能的差異がありますので・・・
>
> ※ 何故、そのようなことをしないといけないのでしょうか?
>
>   どうしても順序を管理したいと言うことであれば、共有プリンター
>   に印字指令を出すのではなく、サーバー側に印字データを受け取る
>   サービスを立て、そこで順番管理を行えば、一度に接続可能なPC
>   は限定できますので、順番は確保できます。
>
>   しかしクライアントPC台数が多い場合、非常に応答性の悪いシス
>   テムになってしまいますので・・・
>
>   ほかにもやり方はあると思いますが、こんなところでしょうか・・・
>
> 以上。参考まで
>

   このプリンタは印刷用のコマンドを送信して、印刷しており、プリンタ自体もプログラムで制御しております。そのため、通常のプリンタのようにスプールにたまるわけではありません。送ったデータをその都度印刷するイメージです。そのため、印刷データを送った順番で印刷したいのです。
> このプリンタは印刷用のコマンドを送信して、印刷しており、プリンタ自体も
> プログラムで制御しております。そのため、通常のプリンタのようにスプール
> にたまるわけではありません。送ったデータをその都度印刷するイメージです。
> そのため、印刷データを送った順番で印刷したいのです。

  そういう前提条件は真っ先に書きましょう!

● で、それだけでは結局解決できませんヨ!
  その特殊なプリンターは何でしょうか?

以上。
■No24909に返信(オショウさんの記事)
>>このプリンタは印刷用のコマンドを送信して、印刷しており、プリンタ自体も
>>プログラムで制御しております。そのため、通常のプリンタのようにスプール
>>にたまるわけではありません。送ったデータをその都度印刷するイメージです。
>>そのため、印刷データを送った順番で印刷したいのです。
>
>   そういう前提条件は真っ先に書きましょう!
>
> ● で、それだけでは結局解決できませんヨ!
>   その特殊なプリンターは何でしょうか?
>
> 以上。

サトーのラベルプリンタです。
> サトーのラベルプリンタです。

  サトーでしたか・・・

● 本題

  整理すると・・・
  1. サーバー側で印刷処理を行うプログラムが稼働
  2. クライアントはサーバーに対し印刷を依頼
    その際にサーバー側は印刷順序を管理する方法として
    ミューテックスを使って排他制御している
  3. サーバー側はクライアントから依頼された順序で印刷
    命令をプリンターに送信

  問題
  クライアントから依頼された印刷指令の順番と実際に印刷
  される順序が異なる。

● まず確認しないといけないこととして・・・
  クライアントAから印字指令を送信した際、サーバー側で
  排他制御が生きて機能しているのかどうか・・・
  クライアントAが接続を解除、もしくは印字指令送信が終わって
  いない状態で、クライアントBが接続し、印字指令送信できない
  のか、できてしまうのか?

  あと仕様として、複数台のプリンターに対し、サーバー側のクラ
  イアントから印刷指令を受け取るネットワーク通信?等のポート
  は、1個?複数?

  サーバー側が複数のクライアントから同時並行で処理を受けつけ
  れる状態だと、ミューテックスの管理に方法に穴(バグ)が無い
  のかどうか・・・

> これで複数プリンタが存在しても、同時に印刷データを送信し、並行して処理
> ができたのですが、同じプリンタに要求があった時に、リクエストがあった順
> 番で印刷ができませんでした。

  これから類推すると、サーバー側が複数のプリンター並列的に印字処理が
  できたとありますが、クライアントからの受信部(スレッド?)と、印字
  部(スレッド?)の同期的処理にバグがあるのでは・・・と考えます。

● 因みに、サーバー側はWindowsサービスですか?
  あと、ミューテックウス使わないで、System.Threadingクラスの
  ReadWriteLock使った方が、よいようにも思いますし・・・
  受信した印字指令の管理は、どうやっておられるのでしょうか?
  配列?ArrayList?
  受信した印刷指令を一旦格納する際、その配列やArrayListにReadWriteLock
  使ってやるだけで、排他制御的順序管理できるはずですが・・・
  完全にFIFO、順序管理を徹底するならば、サーバー側の受信ポートを、
  1個にしてしまえば、一度に受信可能な印字指令は、1クライアントに
  なりますので、順番が変わることにはならないので、バグの個所を探す
  のに有効かも・・・
  (バグが出なくなった場合、そこの同期に問題がある・・・)

以上。
■No24912に返信(オショウさんの記事)
> ● 本題
>
>   整理すると・・・
>   1. サーバー側で印刷処理を行うプログラムが稼働
>   2. クライアントはサーバーに対し印刷を依頼
>     その際にサーバー側は印刷順序を管理する方法として
>     ミューテックスを使って排他制御している
>   3. サーバー側はクライアントから依頼された順序で印刷
>     命令をプリンターに送信
>
>   問題
>   クライアントから依頼された印刷指令の順番と実際に印刷
>   される順序が異なる。
>
> ● まず確認しないといけないこととして・・・
>   クライアントAから印字指令を送信した際、サーバー側で
>   排他制御が生きて機能しているのかどうか・・・
>   クライアントAが接続を解除、もしくは印字指令送信が終わって
>   いない状態で、クライアントBが接続し、印字指令送信できない
>   のか、できてしまうのか?

その際は、スレッド生成をして、ミューテックスの所有権取得待ち状態なので印字指令の送信はできません。

>
>   あと仕様として、複数台のプリンターに対し、サーバー側のクラ
>   イアントから印刷指令を受け取るネットワーク通信?等のポート
>   は、1個?複数?

ポートは1個になります。

>
>   サーバー側が複数のクライアントから同時並行で処理を受けつけ
>   れる状態だと、ミューテックスの管理に方法に穴(バグ)が無い
>   のかどうか・・・
>
>>これで複数プリンタが存在しても、同時に印刷データを送信し、並行して処理
>>ができたのですが、同じプリンタに要求があった時に、リクエストがあった順
>>番で印刷ができませんでした。
>
>   これから類推すると、サーバー側が複数のプリンター並列的に印字処理が
>   できたとありますが、クライアントからの受信部(スレッド?)と、印字
>   部(スレッド?)の同期的処理にバグがあるのでは・・・と考えます。
>
> ● 因みに、サーバー側はWindowsサービスですか?
>   あと、ミューテックウス使わないで、System.Threadingクラスの
>   ReadWriteLock使った方が、よいようにも思いますし・・・
>   受信した印字指令の管理は、どうやっておられるのでしょうか?
>   配列?ArrayList?
>   受信した印刷指令を一旦格納する際、その配列やArrayListにReadWriteLock
>   使ってやるだけで、排他制御的順序管理できるはずですが・・・
>   完全にFIFO、順序管理を徹底するならば、サーバー側の受信ポートを、
>   1個にしてしまえば、一度に受信可能な印字指令は、1クライアントに
>   なりますので、順番が変わることにはならないので、バグの個所を探す
>   のに有効かも・・・
>   (バグが出なくなった場合、そこの同期に問題がある・・・)
>
> 以上。

受信した印字指令はNameValueCollectionに格納しています。
サーバはソケット通信を行い、System.Net.Sockets.TcpClientクラスを使用しています。

ちょっと複雑になってきましたので、詳しい状況を説明します。
<現在のシステム>
ハンディターミナル10台(クライアント)
サーバ1台
サトーラベルプリンタ2台
<処理の流れ>
機能はたくさんあるのですが、主な流れは以下の通りです。
1.ハンディから入力して、サーバに送信。
2.サーバはDBを更新、参照をしてハンディへデータを返信。
3.ハンディは受け取ったデータを表示、ラベル発行機能があるものは受け取ったデータからラベル印刷。
<問題>
ラベル印刷に時間がかかり、印刷時は他の業務ができない。
<回避策>
ラベル印刷機能をサーバ側へ移行し、スレッドを生成して行う(非同期)。
<結果>
スレッド生成をして、排他を行い印刷できることは確認。

<新たな問題点>
データを受信した順番で印刷を行いたいが、印刷順番が不定になってしまう。

<テストプログラムの作成>
Public Class Form1

Delegate Function LabelPrintDelegate(ByVal strIpAddress As String, ByVal intCnt As Integer) As Boolean
Private cnt As Integer = 0

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

cnt = cnt + 1

'デリゲートオブジェクトの作成
Dim clsLabelPrintdlgt As New LabelPrintDelegate(AddressOf LabelPrint)

clsLabelPrintdlgt.BeginInvoke(Me.TextBox1.Text, cnt, Nothing, Nothing)

End Sub

Private Function LabelPrint(ByVal strIpAddress As String, ByVal intCnt As Integer) As Boolean

Dim mutex As System.Threading.Mutex = New System.Threading.Mutex(False, strIpAddress)

Try

'ミューテックスの所有権を要求する
mutex.WaitOne()

Debug.WriteLine(intCnt.ToString & "回目のスレッド")

System.Threading.Thread.Sleep(30000)

Catch ex As Exception

Finally

'ミューテックスを解放する
mutex.ReleaseMutex()

End Try

End Function

End Class

テキストボックス1つとボタン1つのフォームを作成。
テキストボックスにはラベルプリンタのIPを入力。
同じIPを入力した状態で、ボタンを何度か押す。
これで排他制御の確認ができるが、イミディエイトウインドウに表示される文字は1回目、2回目、3回目というように順番にはなりません。

この制御が1回目、2回目、3回目というように順番に制御できないかと質問させていただきました。
> <現在のシステム>
> ハンディターミナル10台(クライアント)
> サーバ1台
> サトーラベルプリンタ2台

  あら〜どこのハンディ?
  私もラベル印刷た貼付機の制御ソフト作ってまして、その構成に
  非常に似たものの製作・納品の経験あります。(現在も稼働・・・)

> <処理の流れ>
> 機能はたくさんあるのですが、主な流れは以下の通りです。
> 1.ハンディから入力して、サーバに送信。
> 2.サーバはDBを更新、参照をしてハンディへデータを返信。
> 3.ハンディは受け取ったデータを表示、ラベル発行機能があるものは受け取ったデータからラベル印刷。
> <問題>
> ラベル印刷に時間がかかり、印刷時は他の業務ができない。
> <回避策>
> ラベル印刷機能をサーバ側へ移行し、スレッドを生成して行う(非同期)。
> <結果>
> スレッド生成をして、排他を行い印刷できることは確認。
>
> <新たな問題点>
> データを受信した順番で印刷を行いたいが、印刷順番が不定になってしまう。

  ハンディーからの印刷指令受信機構でのロギングとデバッグは
  十分になされてます?

  毎回順番が変化するのではなく、何個か毎に(ランダムではあるが)
  印刷順序が狂うと言う状況ではないのでしょうか。

> テキストボックス1つとボタン1つのフォームを作成。
> テキストボックスにはラベルプリンタのIPを入力。
> 同じIPを入力した状態で、ボタンを何度か押す。
> これで排他制御の確認ができるが、イミディエイトウインドウに表示される文字は1回目、2回目、3回目というように順番にはなりません。

  やっぱり・・・ハンディーからのTCP/IP通信部分に問題あろうかと。

  私も遭遇しました。
  ハンディー用の通信ライブラリか何か提供されてません?
  ただ単に受信するだけで、送信側はハンディーにスクリプト書いて
  登録するタイプ?

  1回毎にハンディーとの通信部分、Shutdownしてセッションを閉じ
  Listenするようになってます?

以上。
ハンディとかプリンタの機種には関係ない話題では?



Mutexでの権利取得の場合、どのスレッドから再開されるかは明確に定義されていなかったと思います。
このため、今の方針で順番を制御することは困難です。
(2つ以上のスレッドがMutexの権利待ちをしていたとしても、先に待っていた方が先に実行されるとは限らない)

スレッドをたくさん作って排他して止めるのではなく、プリンタ毎にスレッドを作って、1つのプリンタには1つのスレッドでのみ印刷を行うようにすれば良いのではないでしょうか?

印刷用スレッドはスプールに相当するキューを持っておき、先頭から順番に印刷処理を行うようにし、印刷を依頼する側はキューの最後に印刷依頼を詰んでおくという形です。
(キューが空の場合は、キューが詰まれるまでスレッドはSleepなりさせておく)

もっとも、キューは印刷用スレッドと依頼側のスレッドの両方から同時にアクセスされる恐れがあるため、必要最小限の排他を加える必要があります。
■No24915に返信(オショウさんの記事)
>   あら〜どこのハンディ?
>   私もラベル印刷た貼付機の制御ソフト作ってまして、その構成に
>   非常に似たものの製作・納品の経験あります。(現在も稼働・・・)
>
CASIOのハンディを使用しています。

>   ハンディーからの印刷指令受信機構でのロギングとデバッグは
>   十分になされてます?
>
>   毎回順番が変化するのではなく、何個か毎に(ランダムではあるが)
>   印刷順序が狂うと言う状況ではないのでしょうか。
>
印刷関連のテストは十分行っております。

>   やっぱり・・・ハンディーからのTCP/IP通信部分に問題あろうかと。
>
>   私も遭遇しました。
>   ハンディー用の通信ライブラリか何か提供されてません?
>   ただ単に受信するだけで、送信側はハンディーにスクリプト書いて
>   登録するタイプ?
>
>   1回毎にハンディーとの通信部分、Shutdownしてセッションを閉じ
>   Listenするようになってます?
>
ハンディ用の通信ライブラリは使用しておりません。
TcpClientのAccept、Closeはしております。
■No24919に返信(Azuleanさんの記事)

Azulean様。回答ありがとうございます。

> ハンディとかプリンタの機種には関係ない話題では?
>
>
>
> Mutexでの権利取得の場合、どのスレッドから再開されるかは明確に定義されていなかったと思います。
> このため、今の方針で順番を制御することは困難です。
> (2つ以上のスレッドがMutexの権利待ちをしていたとしても、先に待っていた方が先に実行されるとは限らない)
>
やはりそうでしたか。Mutexの権利取得が不定なのでそうなのではないかと思っておりました。

> スレッドをたくさん作って排他して止めるのではなく、プリンタ毎にスレッドを作って、1つのプリンタには1つのスレッドでのみ印刷を行うようにすれば良いのではないでしょうか?
>
> 印刷用スレッドはスプールに相当するキューを持っておき、先頭から順番に印刷処理を行うようにし、印刷を依頼する側はキューの最後に印刷依頼を詰んでおくという形です。
> (キューが空の場合は、キューが詰まれるまでスレッドはSleepなりさせておく)
>
> もっとも、キューは印刷用スレッドと依頼側のスレッドの両方から同時にアクセスされる恐れがあるため、必要最小限の排他を加える必要があります。

キューを使用して行うのですか。これはThreadPoolクラスを使用するということでしょうか?独自でキューを用意して処理するということでしょうか?VB.net初心者でわからないことばかりで申し訳ありません。
> CASIOのハンディを使用しています。

  最近CASIOは使っていないので・・・

> ハンディ用の通信ライブラリは使用しておりません。
> TcpClientのAccept、Closeはしております。

  使わなかった場合の通信の性能保証はどうなりますか?

  あと、TcpClientでClose時にFINパケット流れましたっけ?
  ハンディ側とのTCP/IP通信をモニターして動作確認した方
  が、なんとなくよいように思います。

  要は順序が正しく印刷されない場合のハンディーとの通信
  が、想定していた通りの動作となっているか・・・

※ Welcatのハンディ使った折、提供されている通信用SDKが、
  .NETで使用できなかったので、TCP/IPモニター入れて解析
  し、Socket通信で独自にインプリしたことあります。
  普通(正常)に動作している時はよいのですが、たまに異
  常なパケットが流れ、それによって異常動作がありました
  ので、その異常パケットに対する応答をどうしたら正常化
  するか、結構骨を折ったもので・・・

以上。参考まで
> キューを使用して行うのですか。これはThreadPoolクラスを使用するということでしょうか?独自でキューを用意して処理するということでしょうか?VB.net初心者でわからないことばかりで申し訳ありません。

  印字情報を独自の構造体作って代入し、ArrayListにでも.Addすれば
  済む話かと。

  あと、先にも書きましたが、ReadWriteLockで十分かと・・・

※ プリンター分のArryListがあればOK〜

以上。
■No24926に返信(オショウさんの記事)
>>CASIOのハンディを使用しています。
>
>   最近CASIOは使っていないので・・・
>
>>ハンディ用の通信ライブラリは使用しておりません。
>>TcpClientのAccept、Closeはしております。
>
>   使わなかった場合の通信の性能保証はどうなりますか?
>
>   あと、TcpClientでClose時にFINパケット流れましたっけ?
>   ハンディ側とのTCP/IP通信をモニターして動作確認した方
>   が、なんとなくよいように思います。
>
>   要は順序が正しく印刷されない場合のハンディーとの通信
>   が、想定していた通りの動作となっているか・・・
>
> ※ Welcatのハンディ使った折、提供されている通信用SDKが、
>   .NETで使用できなかったので、TCP/IPモニター入れて解析
>   し、Socket通信で独自にインプリしたことあります。
>   普通(正常)に動作している時はよいのですが、たまに異
>   常なパケットが流れ、それによって異常動作がありました
>   ので、その異常パケットに対する応答をどうしたら正常化
>   するか、結構骨を折ったもので・・・
>
> 以上。参考まで

サーバ側で印刷情報は正しく取れているので問題ないと思います。
TcpClientClose時にFINパケット流れるかはわかりません。知識不足で申し訳ございません。
プリンタ毎のスレッドを生成する方法で一度考えてみます。
オショウ様
いろいろな貴重なご意見ありがとうございました。
2009/07/10(Fri) 08:36:06 編集(投稿者)

> サーバ側で印刷情報は正しく取れているので問題ないと思います。

  取れるのは最低限で、ソケットと印字順序の同期の部分で
  問題はないかと・・・

> TcpClientClose時にFINパケット流れるかはわかりません。知識不足で申し訳ございません。

  DX-950/930/870 は、SS無線で実際のところ他社ハンディのSS無線でも
  無茶苦茶問題があって2年ごしでメーカーに改善調査させましたが、改善
  しなかったもので、結果その製品は廃版になってしまいました。

  DT-X7 は問題ないかナ〜
  ※ こちらの機種をお使い?・・・

※ 無線LANタイプにせよ、障害物やコンクリ−ト壁による乱反射で
  思わぬトラブルが出ます。
  通常は電波を強くして通信の性能確保するんですが、微弱にして回避
  できるケースもあります。

  複数台を同時に使用する場合、1台では問題出ずで、複数台でわけわ
  からんトラブルが出るケースは、大抵メーカーは対応してくれないの
  で注意と言うか、こちらにそれなりのスキルが無いと正常稼働にこぎつ
  けません。

  どこが主原因が、必ずつきとめて下さい!

以上。

> プリンタ毎のスレッドを生成する方法で一度考えてみます。
> オショウ様
> いろいろな貴重なご意見ありがとうございました。
■No24932に返信(オショウさんの記事)
>   どこが主原因が、必ずつきとめて下さい!

ハード側の事情が影響することも確かにありますが、現状はスレッドを複数動作させていること、それらのスレッドに順位付けはないことから起きているという説が有力だと思っています。
それを確かめることなく、「ハードが悪いんだよ!」みたいな流れはちょっと早計ではないでしょうか。
質問者を混乱させる可能性もあります。


実際、 No.24913 でTCP/IPやハード層と関係ないところで起きることを検証されています。
故に、まずはソフトでの問題の可能性を疑うべきです。
オショウ様
Azulean様

いろいろと回答ありがとうございました。
業務の都合で一旦この開発は中止となりましたので、解決済みとさせていただきます。
毎回スレッドを作成するのではなく、プリンタ毎にスレッドを作成し、Mutexで制御をしない方法で考えたいと思います。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板