Kenrowの覚書と日々

細かい事を覚えられないため、細かい事をどんどん書いていく場

 

IRST 14.6以降で、NVMEデバイスに対してNVMEコマンドを直接発行するAPIです。

(Adminコマンドも通ります。)

検証用に作ったテストコードからの抜粋なのでそのままじゃコンパイル出来ません。

あくまでもAPI&構造体の使用例として察してください。

 

CSMINVM.H

//
// Intel Rapid Storage Technology : NVMe Pass-through Support
// for IRST 14.8 Later
// Revision 0.8 : NVMe Pass-Through API

#pragma once

#include “resource.h”

#include “nvme.h”

// IRST NVMe Passthrough IOCTL API Definitions

#define INTELNVM_SIGNATURE “IntelNvm”
#define NVME_PASS_THROUGH_VERSION 1
#define IOCTL_NVME_PASS_THROUGH CTL_CODE(0xF000, 0xA02, METHOD_BUFFERED, FILE_ANY_ACCESS)

#pragma pack(4)
struct NVME_PASS_THROUGH_PARAMETERS {
NVME_COMMAND Command;    //GENERIC_COMMAND Command;
BOOLEAN IsIOCommandSet;
NVME_COMPLETION_ENTRY Completion;    //COMPLETION_QUEUE_ENTRY Completion;
ULONG DataBufferOffset;
ULONG DataBufferLength;
ULONG Reserved[10];
};
#pragma pack()

#pragma pack(1)
struct NVME_IOCTL_PASS_THROUGH {
SRB_IO_CONTROL Header;
UCHAR Version;
UCHAR PathID;
UCHAR TargetID;
UCHAR Lun;
NVME_PASS_THROUGH_PARAMETERS Parameters;
};
#pragma pack()

RaidInterface.cpp

INT CRaidInterface::sendNvmCommand(int port, int pathid, BYTE cmd, BYTE param, PBYTE pResultBuffer, DWORD ResultBufferSize)
{
LOG(0, “IN”);

LOG(0, “Scsi Port = %d, PathID = %d”, port, pathid);
LOG(0, “Command = 0x%02x, Param = 0x%02x”, cmd, param);

INT iRc = 0;
BOOL bRc;

// Device Open
HANDLE m_hHandle = open(port);
if(m_hDevice == INVALID_HANDLE_VALUE)
{
LOG(0, “OUT – Failed open device handle”);
return    FALSE;
}
ASSERT(m_hDevice);

// First let’s allocate memory for buffer.To do so we must first calculate eventual offset with padding :
ULONG offset = ((sizeof(NVME_IOCTL_PASS_THROUGH) – 1) / sizeof(DWORD) + 1) * sizeof(DWORD);
ULONG ioctlBufferSize = offset + sizeof(NVME_IDENTIFY_CONTROLLER_DATA);
BYTE* buf = (BYTE*)VirtualAlloc(NULL, ioctlBufferSize, MEM_COMMIT, PAGE_READWRITE);
ZeroMemory(buf, ioctlBufferSize);

// Prepare header field :
SRB_IO_CONTROL &info = *reinterpret_cast<SRB_IO_CONTROL*>(buf);
info.HeaderLength = sizeof(SRB_IO_CONTROL);
info.ControlCode = IOCTL_NVME_PASS_THROUGH;
info.Length = ioctlBufferSize – sizeof(SRB_IO_CONTROL);
info.Timeout = 500;
memcpy_s(&info.Signature, sizeof(info.Signature), INTELNVM_SIGNATURE, sizeof(info.Signature));
info.ReturnCode = 0;

// Fill version and PathID :
NVME_IOCTL_PASS_THROUGH &ioctl = *reinterpret_cast<NVME_IOCTL_PASS_THROUGH*>(buf);
ioctl.Version = NVME_PASS_THROUGH_VERSION;
ioctl.PathID = pathid;
ioctl.TargetID = 0;
ioctl.Lun = 0;

// Fill Parameters field :
NVME_PASS_THROUGH_PARAMETERS &parameters = *reinterpret_cast<NVME_PASS_THROUGH_PARAMETERS*>(&ioctl.Parameters);
parameters.Command.CDW0.OPC = cmd;
parameters.Command.CDW0.FUSE = NVME_FUSED_OPERATION_NORMAL;
parameters.Command.NSID = 0;    // NVME_NAMESPACE_ALL;;
parameters.Command.u.IDENTIFY.CDW10.AsUlong = NVME_IDENTIFY_CNS_CONTROLLER;
parameters.DataBufferLength = sizeof(NVME_IDENTIFY_CONTROLLER_DATA);
parameters.DataBufferOffset = offset;

LOG(1, “Ioctl Buffer Length = 0x%04x”, ioctlBufferSize);
LOG(1, “SRB_IO_CONTROL.HeaderLength = 0x%04x”, info.HeaderLength);
LOG(1, “SRB_IO_CONTROL.Length= 0x%04x”, info.Length);
LOG(1, “Parameters.DataBufferLength = 0x%04x”, parameters.DataBufferLength);
LOG(1, “Parameters.DataBufferOffset = 0x%04x”, parameters.DataBufferOffset);

//LOG(1, “SRB_IO_CONTROL Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Header));
//LOG(1, “NVME.Version Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Version));
//LOG(1, “NVME.PathID Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, PathID));
//LOG(1, “NVME.TargetID Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, TargetID));
//LOG(1, “NVME.Param Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters));
//LOG(1, “NVME.Param.Command Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, Command));
//LOG(1, “NVME.Param.IsIOCommandSetOffset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, IsIOCommandSet));
//LOG(1, “NVME.Param.Completion Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, Completion));
//LOG(1, “NVME.Param.DataBuffOffset Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, DataBufferOffset));
//LOG(1, “NVME.Param.DataBuffLengtht Offset = 0x%04x”, offsetof(struct NVME_IOCTL_PASS_THROUGH, Parameters) + offsetof(struct NVME_PASS_THROUGH_PARAMETERS, DataBufferLength));
//LOG(1, “sizeof(NVME_COMMAND) = 0x%04x”, sizeof(NVME_COMMAND));

LOG(1, “Request/Parameters Buffer”);
Logger::BinaryDump((BYTE*)&info, sizeof(NVME_IOCTL_PASS_THROUGH));

bRc = sendCsmiCommand(m_hDevice, IOCTL_NVME_PASS_THROUGH, (SRB_IO_CONTROL*)buf, ioctlBufferSize);
LOG(0, “send(CSMI NVME PASS THROUGH) rc=%d”, bRc);

LOG(1, “Buffer Dump after Command Exec”);
Logger::BinaryDump((BYTE*)buf, ioctlBufferSize);

NVME_IDENTIFY_CONTROLLER_DATA DataBuffer = *reinterpret_cast<NVME_IDENTIFY_CONTROLLER_DATA*>(buf + parameters.DataBufferOffset);
LOG(1, “Buffer Address = %08p\nResult Adrress = %08p”, &buf, &DataBuffer);

if (bRc == 1)
{
// IDENTIFY以外のコマンドに対応する場合は分岐が必要
//if(cmd != ATA_SET_FEATURES && pResultBuffer != NULL)
{
LOG(1, “Copy NVME IDENTIFY DATA”);
memcpy_s(pResultBuffer, ResultBufferSize, &DataBuffer, ResultBufferSize);

LOG(1, “Result CSMI_NVME_PASS_THROUGH”);
Logger::BinaryDump((BYTE*)pResultBuffer, ResultBufferSize);
}
}
else
{
long err = GetLastError();
LOG(0, “sendCsmiCommand(CSMI_NVME_PASS_THROUGH) return code = %d”, err);
LOG(0, “buf->IoctlHeader.ReturnCode = %d”, info.ReturnCode);
if (info.ReturnCode != 0)
iRc = info.ReturnCode;
else
iRc = -2;
}
VirtualFree(buf, 0, MEM_RELEASE);
close();

return    iRc;
}

INT CRaidInterface::sendNvmCommand(CRaidDrive* p, BYTE cmd, BYTE param, PBYTE pInBuffer, DWORD BufferSize)
{
if (p != NULL && pInBuffer != NULL && BufferSize >= sizeof(SRB_IO_CONTROL))
return sendNvmCommand(p->m_ScsiPort, p->m_PortID, cmd, param, pInBuffer, BufferSize);
return -2;
}

SAMPLE CODE:

PBYTE cBuffer2 = (PBYTE)VirtualAlloc(NULL, sizeof(NVME_IDENTIFY_CONTROLLER_DATA), MEM_COMMIT, PAGE_READWRITE);
ZeroMemory(cBuffer2, sizeof(NVME_IDENTIFY_CONTROLLER_DATA));

LOG(0, “NVME ADMIN COMMAND”);
iRc = raidInterface.sendNvmCommand(raidDrive, NVME_ADMIN_COMMAND_IDENTIFY, 0x00, (PBYTE)cBuffer2, sizeof(NVME_IDENTIFY_CONTROLLER_DATA));
//if (iRc == 0)
{
// 取得したドライブ情報を保存
Logger::BinaryDump((BYTE*)cBuffer2, sizeof(NVME_IDENTIFY_CONTROLLER_DATA));
CNVMeParser::parseID((BYTE*)cBuffer2, raidDrive->m_driveID); // PARSE

raidDrive->m_driveID.m_Protocol = PROTOCOL_TYPE_CSMI_NVM;
raidDrive->m_Environment = 3;    // NVMe(IRST)
raidDrive->m_BusType = 0x11;    // NVMe
//raidDrive->m_DeviceType = 0;
//raidDrive->m_ScsiPortProtocol = CSMI_SAS_PROTOCOL_SATA;
}
m_raidDrivesList.Add(raidDrive);

VirtualFree(cBuffer2, 0, MEM_RELEASE);

 

元キット

プラッツ  38(t) カメさんチームVer

 

劇中との差異

劇中の車両は(大雑把には)B/C型、キットのE/F/G型

・砲塔の同軸機銃の前面装甲形状が違う。(B型)

・無線手席の前面装甲が後退している(B型)

・無線手席の前面装甲が後退した分、ギアボックス上の装甲を延長(B型)

・操縦主席斜め前の装甲の面取りが無い(D以降風)

・前面装甲の位置に併せて、フェンダーを吊り下げる強化リブの位置が後退している。

(フェンダー上の凸モールドの切れ目を移動)

・車体側面のペリスコープカバーを左右同じ形状にする

・車体後部のマフラーの位置は上(D以降風)

・各部リベット位置の修正

・車体後部の牽引ワイヤーを追加(キットでは省略されている)

 

 

製作途中。有る程度進んだらゴールドエディションと平行で作業する予定。

ゴールドエディションが素組必須なので、こっちは劇中車両を再現しようとしたら・・・地味なのに手間がかかる修正ばかりで(汗)

 

元キット

ドラゴン ヤークトティーガー

 

劇中との差異

・予備履帯無し(ラックのみ)

・牽引ワイヤー無し

・戦闘室後部の対空機銃(MG42)は無し。支柱のみ装備

 

MG誌模型戦車道選手権応募作品。

最初のネタの4号D型が間に合わないのが判った時点(締め切り2週間前)に急遽製作開始した作品。

元キットが結構古いので細かい所をチョコチョコ手を入れた以外はほぼ素組み。

時間も無かったので細かい塗装に凝れなかったのも心残りかなぁ

塗装は以前からやりたかったオキサイドレッド単色。アクセントとして、OVMは丁度購入したばかりの「木目塗料セット」を使って派手目の塗装をしてみました。

エンジングリルのエッチングは凹みを作って面に馴染ませた上で黒でドライブラシ。

意図してなかったのですが、このドライブラシが煤とゆうよりは下に空間があるって感じに仕上ってくれました。

本当は履帯に10話の戦闘開始直後の森を突破してきた時に噛んでるはずの小枝等をウェザリングの一環として組み込みたかったのですが、丁度いい素材にめぐり合えていないので断念しています・・・

なんか良いの無いですかねぇ。木材とかなら適当な枝を持ってくればいいのですが、1/35での小枝サイズとなると・・・サイズ的には糸くずと大差無いですからねぇ

かといって本当に糸くずを使うわけにも行かないし(色や質感のコントロールが難しい)

元キット

プラッツ 4号戦車D型あんこうチームVer

トランペッター 4号ベルゲーパンツァー

 

製作途中。

MG誌の模型戦車道選手権の応募作品用にと製作を開始したのですが、「絶対に間に合わない」と判ったので製作中断。

以前作ったのと比べて、今回のは「アフターパーツを惜しまない」「内部再現も行う」のがポイント

エンジン・戦闘室・操縦室等のパーツをトランペッターのキットから移植し、更に練習試合仕様の特色であるクッションなどの小物も作ろうとゆう計画です。

 

で、エンジンとかを組み込むのを楽しみすぎてスケジュール崩壊w

すり合わせの犠牲として、砲塔下の燃料タンクのサイズを拡張したのですが、その上面に貼りこむ滑り止めパターンのパーツが調達出来ないのでどっちにしても間に合わなかったのですが。

砲弾ラックに積み込む砲弾も大量に作るか調達しないとならんのですが、75mm単砲身(24口径)用の砲弾ってアフターパーツとして販売されてるのは数少ないんですよね。

(滑り止めパターンのエッチングと共に取り寄せ中ですが)

しかしまぁ、こうやって内部を作りこむと本当に戦車ってギッチギチに詰め込まれてるんだね~と実感できます。

エンジン等を移植する段階でキットそのままの車体パーツの厚みじゃ空間が足りなくて薄く削りこむ作業がまた大変でした。

 

後は完成後でも内部を見れるようにと装甲板を脱着出来るように組むのがねぇ。

戦車模型って接着する事でパーツが互いに差さえあって歪みを修正するような所が多いので分割可能にしておくと歪みが補正されなくて・・・

まだまだ色々と仕込みしないとならんようです(一応ネオジム磁石は埋め込もうかと思って確保はしてあります)

元キット

タミヤ 1/48 イギリス歩兵戦車 マチルダMk.III/IV

劇中との差異

・予備履帯無し、ラックのみ

・砲塔横の装備品無し(ラックも無し)

・砲塔後部のアンテナ基部の形状が異なる(アメリカ軍仕様?)

 

ほぼキットのままでOK

予備履帯ラックはアルミテープを重ねて再現。すぐにヘロヘロになってしまうので適当なエッチングパーツの余りを加工すべきだったかも。

ディテールアップは排気マフラーの開口、フェンダー前部のライトガードの支柱を追加と、小スケールな事もあってかなり適当な工作。

製作当時はまだ放送中でデカールが存在しなかったので、校章デザインの元絵を入手してデカールを自作。

八九式とセットにして情景を作りたいので1/35も作りたいなぁ

元キット

プラッツ 4号戦車D型あんこうチームVer

劇中との差異

・履帯がH/J型後期に使われた滑り止めパターン&センターガイドに肉抜きが無いタイプがセットされている

(劇中では中期型、滑り止めパターン無し、センターガイド肉抜き有りのタイプ)

・予備履帯有り。前面8枚、側面4枚

・車長ハッチ内のペリスコープがG型に使われてたタイプ(内部だけ)

・フェンダー上の工具箱は無し

・OVM配置が微妙に違う

・砲塔背面、車長ハッチの出っ張り部分のリベットが2つ有り

・砲塔背面のピストルポートが左右対称の向きになっている

・車体背面、牽引ワイヤー留め具の位置がF型以降の配置になっている

 

模型初心者に配慮して製作難易度を下げてはいるのだが・・・

元になったキットが製作難易度高めのドラゴンの中でも特に難易度高いとゆうキットなので、決して初心者向けとは言えない結果になっている上に、劇中との差異も多いとゆうちょっと困ったキット。

精査すると努力したとゆうのは認められるが残念な結果になってるのは・・・

もっとも完全再現は、ほとんどのランナーを新規金型を作らないとならないので無理ですね。

 

まぁ、外国メーカーの製品で日本のアニメのキットを販売してしまうって事自体がミラクルなんですがw

 

元キット

ファインモールド 八九式中戦車甲型あひるさんチームVer

劇中との差異

・排気マフラーのカバーが網タイプでは無く、パンチ穴タイプ

 

劇中のデザインが実存車両を元に作画設定が起こされているだけに、登場車両中で一番キットが忠実に再現されている。

キット自体も組み易く、基本を抑えてれば初心者モデラーでも十分見栄えのするものが組みあがる。

注意すべきことは、練習試合(聖グロリアーナ戦)仕様にすると大判のデカールをリベットの凸モールドが沢山ある面に貼る必要が出てくる事。

白地で隠蔽性を確保したデカールなので厚みも硬さもそれなりにあるので、曲面程度ならともかくリベットモールドの上に貼って馴染ませるのは難易度がかなり高い。

痛車・痛飛行機模型等の経験が無い場合は、素直に発見時または本戦仕様で組むのが懸命。

まぁ、塗装自体は一緒なので、まずはやるだけやってみて失敗したら剥がして本戦仕様とゆうのが一番かも。

 

「バッチ処理」とはコンピューター上で無人で自動処理を行うプログラムの総称です。

主にサーバー管理やホストコンピューターのメンテナンスなどで使われます。

具体的には、古い不要になったログファイルを自動的に削除したり、定期的にバックアップ処理を行ったりするのに使われます。

 

使用されるプログラミング言語は「スクリプト言語」と呼ばれる種類の言語が使われるのが一般的です。

とにかく構造がシンプルで処理の変更がその場で出来るとゆうが必須条件、反面あまり複雑な処理は要求されません。

時代の移り変わりもありますが、実行環境により言語がマチマチなのも特徴ともいえます。

DOSや初期のWindowsだと「バッチファイル」

Windows XP、WindowsServer2003/2008世代だと「Windows Script Host」(”WSH”)

OS/2やIBMのホストシステムだと「REXX」

UNIXやLinuxだと「シェルスクリプト」

Webサーバー上で動くcgiなんかでは「Perl」や「PHP」など

他にも、8ビットMircrosoft ExcelやWord上なんかで動く「VBA」なんかも含めても良いかもしれません。

 

どれもこれもテキストファイルにプログラムを書いて、コンパイル作業無しに直接実行ってのが共通しています。

(内部的にはコンパイルして処理速度を速めているものもありますが)

で、今あれこれ試行錯誤しているのが「Windows PowerShell」

WindowsServer2008から提供されはじめたWSHに代わるスクリプト環境です。

今現在、ソフトデザイン&プログラミングを担当しているシステムの動作OSがWindows7(ロボット制御カードを積んでる関係で32ビット版ですが)なので、どうせならPowerShellでメンテナンススクリプト組んでみようかと思いたって環境として評価も兼ねて色々試してる最中です。

で、WSHがVisual BASICやVBAをベースにした言語構造なのに対し、PowerShellはUNIX/Linuxのシェルスクリプトにかなり近づけた文法構造を持ってます。

(恐らく、Linuxサーバーでのメンテナンススクリプトの作成に慣れてるサーバー管理者にとって使いやすいようにとの事でしょう)

実際使ってみると、結構クセあるので最初は戸惑いますね。

しかし、スクリプトファイルで実行の他、コマンドラインで直接命令を実行して動きを確認出来るのは動きを確認しながらスクリプトを書くのに便利だし

コマンドライン上で正規表現のマッチングや抽出なども確認出来るのは結構便利です。

ログの抽出程度なら、スクリプト組まなくても直接正規表現で検索も出来ますし・・・

 

コマンドラインからのPowerShell環境の起動: >powershell

コマンドラインからのPowerShellスクリプトの実行:>powershell  ./script.ps1

PowerShell上からのスクリプト実行: >./script.ps1

PowerShellの終了:>exit

変数: $変数名

変数のキャスト・型変換: $s = [string]$i

行単位のコメント: # 以下行末までコメント

ブロック単位のコメント: <# ….. #>

 

以外にとっかかりになるこの辺の情報を簡潔にまとめてる本やサイトって無いんですよね。

Perlとかシェルスクリプトを弄った経験がある人なら、ここら辺の情報さえ頭に入れちゃえば後は言語リファレンスを片手に色々できる様になると思います。

 

VS2010 + C# + .NET Framework 3.5 で組まれたシステムで、徐々に動作が重くなるとゆう問題が発生しました。

それも特に何もやってないメソッドから受け取った実行結果詳細をログに出力するってところで一行出力するたびに数msかかってしまうとゆうモノ

詳細を調べると処理を繰り返すうちあたかもCPUのクロックが激減したかのようにに全体のパフォーマンスが落ちていってます。

最初は10行出力しても1msさえかかってなかったログ出力が、一行出力するのに数msかかってしまうようになってます。

 

バグの症状としては定番なんですが、定番だけに症例が多いので特定も楽だとは思ったのですが・・・

メモリーリーク問題なし

ゾンビスレッドなし

バックグラウンドタスク問題なし

常駐プログラム問題なし

サービス動作状況問題なし

 

定番の原因が全て「問題なし」

 

ただ気になったのがC#のスレッド絡みでよく見る「スレッドの作成・破棄を繰り返すと効率が悪いので、スレッドプールを使うべき」とゆう一文

私は「スレッドを新規作成するコンストラクタはかなりヘビーなコストがかかる上にリソース消費も激しい」と理解していたのですが、なんとなくひっかかる気もしたのでサンプルコードを作って試験してみました。

                // スレッド作成
Thread threadQuery = new Thread(QueryThread);

// パラメーター作成
object[] prms = new object[] { (string)filename, (string)serialnum, (bool)continuous, (int)interval, (int)timeout };

//    スレッド始動
threadQuery.Start(prms);
handle = threadQuery;

//    スレッド終了
while (handle.IsAlive)
{
System.Threading.Thread.Sleep(10);
}
//    スレッド破棄

//    ログ出力1
WRITE_LOG(“LOG ” + i);
WRITE_LOG(“LOG ” + i);
WRITE_LOG(“LOG ” + i);
WRITE_LOG(“LOG ” + i);

このコードを1000回繰り返して、ログ出力をチェックします。

スレッドの中身は特に何もやらずに10msだけSleepした後にスレッド終了&消滅

で、実行結果(重複行はエディターで削除。つまり1ms以内に終わってる処理を消える)

2012/06/20 10:53:01.155    INFO,LOG 1
2012/06/20 10:53:01.192    INFO,LOG 2
2012/06/20 10:53:01.220    INFO,LOG 3
2012/06/20 10:53:01.221    INFO,LOG 3
2012/06/20 10:53:01.273    INFO,LOG 4
2012/06/20 10:53:01.496    INFO,LOG 5
2012/06/20 10:53:01.614    INFO,LOG 6
2012/06/20 10:53:01.647    INFO,LOG 7
2012/06/20 10:53:01.673    INFO,LOG 8
2012/06/20 10:53:01.697    INFO,LOG 9

.

.

.

2012/06/20 10:53:51.859    INFO,LOG 996
2012/06/20 10:53:51.860    INFO,LOG 996
2012/06/20 10:53:51.888    INFO,LOG 997
2012/06/20 10:53:51.912    INFO,LOG 998
2012/06/20 10:53:51.913    INFO,LOG 998
2012/06/20 10:53:51.935    INFO,LOG 999
2012/06/20 10:53:51.937    INFO,LOG 999
2012/06/20 10:53:51.937    INFO,END

明らかに終盤のログ出力処理間に時間がかかるようになってます。

ログ出力のタイミング次第では1ms差位は出てしまいますが、連続して出てしまうとゆうのは明らかに処理が重くなっている証拠です。

1ms未満の時間は取得出来ないのではっきりとはしないのですがカウントが100付近から遅延の影響が出てきていました。

ちなみに1プロセス内限定の症状のようで、一旦プログラムを終了させ頭から再実行させると遅延はリセットされます。

取り合えずスレッドプールで同じ事をやってみて比較してみようと思います。

 追記:

スレッドプールで同じ事をやってみたら、再現しない事を確認。

通常のスレッド生成と同じとはいかないので多少ロジックは変わっているので単に比較出来ないのですが、ログ上ではパスレッド自体の処理パフォーマンスが向上している事も確認。

ThreadからThreadPoolに書き換えるネックは個別スレッドのハンドルを取得できない事と、それに伴いIsAlive等のプロパティが使えないのが大きいです。

ボリュームの大きい処理をスレッドに投げて、メインはIsAliveで終了したかを確認しつつ他の事をやる・・・とゆう手順を少し弄る必要が出てきます。

自滅型スレッドの場合、スレッド開始に使ったハンドルのIsAliveプロパティをチェックする事で動作が終わったかを判断しますが、スレッドプールを使うとそれが出来ない!

仕方ないのでスレッド内部の始動&終了前にフラグをセットしてそれで判断させる事になります。

そうすると問題になるのは並行して幾つものスレッドを流す場合。

フラグに使う変数が一つだとバッティングしてしまいます。

なので、フラグを配列にしてスレッドIDなり通し番号なりをキーにして管理する事になります。

今回は検索処理を行うスレッドなので、フラグ配列を連想配列で宣言して検索に使うキーをの配列の添え字として使う事で動作完了フラグとして使ってみました。

 

それはある時、唐突に起こりました。

メインスレッドでGUI制御を行い、背景で多数のスレッドが動くアプリケーションの機能拡張&デバッグを行っていました。

そしたら

Invoke(new delegateFullAutoEnd(FullAutoFinished), new object[] { endcode });

上記の行で  例外 FormatException が発生。

FormatExceptionは通常配列の添え字を上限以上に指定してしまった場合に発生する例外なのですが、Invoke周りで発生する要因が思い当たりません。

ちなみにやってる事は、別スレッドで処理を行っている FullAutoスレッドが終了すると イベントを投げて上記のコードを実行、メインスレッドのFullAutoFinished()を返値endcodeを与えて呼び出し、FullAutoスレッドの終了を通知するとゆう物です。

メインスレッドはGUI制御を行っているために、別スレッドから普通にメソッド実行するとエラーになってしまいます。

(別スレッドから呼び出されたpublicメソッドは呼び出しをしたスレッド上で動作するためリソースロック処理を行う必要がある)

そのため、delegateとInvokeを使って間接的にメソッド呼び出しを行い、メインスレッド上でイベント処理を行うとゆう物です。

で、FormatExceptionが発生しそうなのは返値をobjectの配列に格納して渡している部分ですが、これも宣言と異なっていればコンパイル時にエラーになりますし、宣言をチェックしても問題無し・・・

 

取り合えず、初日はお手上げで帰宅。翌日も数時間ああでもないと試行錯誤してたのですが、やはり解決せず。

ここは初心に帰って・・・とメインスレッドから直にFullAutoFinished()をコールしてみようと無理矢理テストコードを書いてみると・・・

問題はFullAutoFinished()の中にありました(汗)

実際にFormatExceptionを出していたのはFullAutoFinished()内の string.Format(”xxxxx”)文でした。

うーん、冗長だから複数行に分割したのが悪かった・・・

string.Format(“A={0} B={1} C={2}”,a,b,c)

みたいに書いてたのを単純に行分割して

string.Format(“A={0}”,a)

string.Format(“B={1}”,b)

string.Format(“C={2}”,c)

って書いちゃってたんですね・・・そりゃアカンわ。

 

Invoke()って、呼び出した先での例外はInvoke()文でThrowされるんですね・・・

まぁ、よく考えるとInvokeってコールバックみたいな動きをするんでこうゆう仕組みになってて当然なんですが、これは知らないと気付かないよなぁ(汗)