2009-07-17 35 views
9

リモート共有(Windowsサーバー上)にファイルの存在をテストしています。テストに使用される基本機能はWinAPIのGetFileAttributesであり、ターゲットサーバーがオフラインになっているとき、権限やDNSに問題があるときなど、さまざまな状況で機能が過度の時間(数十秒)GetFileAttributesでネットワークストールを回避するにはどうすればよいですか?

しかし、私の場合、常にLANにアクセスしているため、1秒未満でファイルにアクセスできない場合、通常は何十秒も待つことでアクセスできなくなります。

停止しないGetFileAttributesの代替手段はありますか? (スレッドで呼び出すとタイムアウト後にスレッドを呼び出すことは別として、それ自体の問題のバッグを持っているようです)

+0

は非同期モデルを除く任意のものを考えるカント。 –

+0

他の状況では、HTTPリクエストを簡単にキャンセル/タイムアウトすることができるため、共有ファイルを提供するためにミニマムウェブサーバーを配備することでこの問題を解決しました。 しかし、この場合、これはさまざまな理由(デプロイメントの問題、セキュリティの問題など)の解決策ではありません。 –

答えて

6

問題は本当にGetFileAttributesではありません。通常は、基本となるファイルシステムドライバへの呼び出しを1回だけ使用します。それは失速しているIOです。

まだ解決策はおそらく簡単でしょう。 1秒後にCancelSynchronousIo()を呼び出します(これは明らかに、最初のスレッドがGetFileAttributes内でスタックされているため、2番目のスレッドが必要です)。

+0

CancelSynchronousIoはWindows XPでは使用できません。 –

+0

@MSalters:GetFileAttributesメソッドの使用中にアクセスが拒否されました(5)エラーが発生しています。ハードウェア構成の低いWindow 2003サーバーがあります。私は完全に機能した他のシステムでは、同じ呼び出しを無効にしてみました。 IOが "アクセスが拒否されました"というエラーを遅らせることができます。 –

+0

@RahulKP:まれではありません。 – MSalters

4

代理人についての1つのクールなことはいつもBeginInvokeEndInvokeです。呼び出されたメソッドが例外をスローしないことを確認してください。[私は]クラッシュ(未処理の例外)が発生すると考えています。

AttributeType attributes = default(AttributeType); 

Action<string> helper = 
    (path) => 
    { 
     try 
     { 
      // GetFileAttributes 
      attributes = result; 
     } 
     catch 
     { 
     } 
    }; 
IAsyncResult asyncResult = helper.BeginInvoke(); 
// whatever 
helper.EndInvoke(); 
// at this point, the attributes local variable has a valid value. 
+1

基本的に、スレッド内でAPI呼び出しをラップしている以外の希望はありませんか?タイムアウト時にスレッドを強制終了することは、経験上、悪いことが起こる可能性があるため、「クリーン」ではないため、スレッド外の解決策が期待されていました。停止スレッドを無視すると、スレッドが停止する可能性があります。 –

+0

申し訳ありませんが、私は間違いなくあなたが.NETで作業していることを前提としていたようです(これ以前のいくつかに回答しました)。 APIに非同期バージョンおよび/またはタイムアウトバージョンがない場合、スレッドソリューションは唯一の信頼できる解決策である可能性があります。 –

0

あなたの最善の解決策は、スレッドプールスレッドを使用して作業を行うことです。

  • ファイルの属性を照会する作業単位を割り当てる
  • は、あなたのスレッド関数が完了すると、完了
  • からGetFileAttributes実行が戻っフォーム
  • に結果を投稿、スレッドは自動的に戻ってみましょうスレッドをプールする必要はありません(スレッドを削除する必要はありません)。

スレッドプールを使用すると、新しいスレッドを作成するコストを節約できます。
あなたはそれらを取り除こうとする悲惨さを救います。

次にあなたがQueueUserWorkItemを使用して、スレッドプールのスレッド上のオブジェクトのメソッドの手順を実行しますあなたの便利なヘルパーメソッドを持っています。

RunInThreadPoolThread(
     GetFileAttributesThreadMethod, 
     TGetFileAttributesData.Create('D:\temp\foo.xml', Self.Handle), 
     WT_EXECUTEDEFAULT); 

あなたがスレッドのデータ情報を保持するオブジェクトを作成します。

TGetFileAttributesData = class(TObject) 
public 
    Filename: string; 
    WndParent: HWND; 
    Attributes: DWORD; 
    constructor Create(Filename: string; WndParent: HWND); 
end; 

とし、スレッドコールバックメソッドを作成します。

procedure TForm1.GetFileAttributesThreadMethod(Data: Pointer); 
var 
    fi: TGetFileAttributesData; 
begin 
    fi := TObject(Data) as TGetFileAttributesData; 
    if fi = nil then 
     Exit; 

    fi.attributes := GetFileAttributes(PWideChar(fi.Filename)); 

    PostMessage(fi.WndParent, WM_GetFileAttributesComplete, NativeUInt(Data), 0); 
end; 

t鶏あなただけのメッセージを処理:

procedure WMGetFileAttributesComplete(var Msg: TMessage); message WM_GetFileAttributesComplete; 

procedure TfrmMain.WMGetFileAttributesComplete(var Msg: TMessage); 
var 
    fi: TGetFileAttributesData; 
begin 
    fi := TObject(Pointer(Msg.WParam)) as TGetFileAttributesData; 
    try 
     ShowMessage(Format('Attributes of "%s": %.8x', [fi.Filename, fi.attributes])); 
    finally 
     fi.Free; 
    end; 
end; 

魔法RunInThreadPoolThreadあなたは、スレッド内のインスタンスメソッドを実行することができます綿毛のほんの少しです:

あなたは上のメソッドを呼び出すことができますラッパーだけですインスタンス変数:リーダーため

TThreadMethod = procedure (Data: Pointer) of object; 

TThreadPoolCallbackContext = class(TObject) 
public 
    ThreadMethod: TThreadMethod; 
    Context: Pointer; 
end; 

function ThreadPoolCallbackFunction(Parameter: Pointer): Integer; stdcall; 
var 
    tpContext: TThreadPoolCallbackContext; 
begin 
    try 
     tpContext := TObject(Parameter) as TThreadPoolCallbackContext; 
    except 
     Result := -1; 
     Exit; 
    end; 
    try 
     tpContext.ThreadMethod(tpContext.Context); 
    finally 
     try 
      tpContext.Free; 
     except 
     end; 
    end; 
    Result := 0; 
end; 

function RunInThreadPoolThread(const ThreadMethod: TThreadMethod; const Data: Pointer; Flags: ULONG): BOOL; 
var 
    tpContext: TThreadPoolCallbackContext; 
begin 
    { 
     Unless you know differently, the flag you want to use is 0 (WT_EXECUTEDEFAULT). 

     If your callback might run for a while you can pass the WT_ExecuteLongFunction flag. 
       Sure, I'm supposed to pass WT_EXECUTELONGFUNCTION if my function takes a long time, but how long is long? 
       http://blogs.msdn.com/b/oldnewthing/archive/2011/12/09/10245808.aspx 

     WT_EXECUTEDEFAULT (0): 
       By default, the callback function is queued to a non-I/O worker thread. 
       The callback function is queued to a thread that uses I/O completion ports, which means they cannot perform 
       an alertable wait. Therefore, if I/O completes and generates an APC, the APC might wait indefinitely because 
       there is no guarantee that the thread will enter an alertable wait state after the callback completes. 
     WT_EXECUTELONGFUNCTION (0x00000010): 
       The callback function can perform a long wait. This flag helps the system to decide if it should create a new thread. 
     WT_EXECUTEINPERSISTENTTHREAD (0x00000080) 
       The callback function is queued to a thread that never terminates. 
       It does not guarantee that the same thread is used each time. This flag should be used only for short tasks 
       or it could affect other timer operations. 
       This flag must be set if the thread calls functions that use APCs. 
       For more information, see Asynchronous Procedure Calls. 
       Note that currently no worker thread is truly persistent, although worker threads do not terminate if there 
       are any pending I/O requests. 
    } 

    tpContext := TThreadPoolCallbackContext.Create; 
    tpContext.ThreadMethod := ThreadMethod; 
    tpContext.Context := Data; 

    Result := QueueUserWorkItem(ThreadPoolCallbackFunction, tpContext, Flags); 
end; 

エクササイズ:番目に指示GetFileAttributesDataオブジェクト内キャンセルフラグを作成しますeスレッドそのはデータオブジェクトを解放しなければならず、ではなく、は親にメッセージを送信します。


それはあなたが作成していると言って、すべての長い道のりです:

DWORD WINAPI GetFileAttributes(
    _In_  LPCTSTR       lpFileName, 
    _Inout_ LPOVERLAPPED     lpOverlapped, 
    _In_  LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine 
); 
+0

プールされたスレッドを使用しても、GetFileAttributesがいくつかのネットワークタイムアウトを待っているため、スレッドが非常に長く停止する可能性があるという問題は解決しません。また、無関係なスレッドを無視する必要があります。たとえば、同じファイルを10秒間に2回クエリすると、1回目のコールは30秒間停止し、2回目はすぐに成功する可能性があります(最初のコールの結果を無視する必要があります)。また、数秒の頻度で複数のファイルを監視する場合、数十、数百のスレッドが停止するのは簡単です...実用的でない:/ –

+0

無関係なスレッドを無視するのは、既にWindowsがネイティブにオーバーラップした(つまり非同期の) 'GetFileAttributesEx 'バージョンを提供しているのと同じです。既存の呼び出しを取り消す必要があります。それは運動によって解決される。あなたの懸念事項は、数十〜数百のスレッドが停止したときに何をすべきかということです。これは懸念事項ではありません。ユーザーの作業項目キューは、古いアイテムがキューからフラッシュされるまでアイテムをキューに入れます。ただし、[あなたがそれに沿って(http://msdn.microsoft.com/en-us/library/windows/desktop/aa363794.aspx)に役立つ何かがスレッドをプールに迅速に返します。 –

+0

'QueueUserWorkItem'があなたのアイテムをキューに入れないようにするのも便利です。何百ものスレッドを作成しません。 'QueueUserWorkItem'の目的の1つは、作業項目を待ち行列に入れることです - スレッドプールは、それらがいつ実行されるかを決定します。 –

関連する問題