13

私は C# プロジェクトに取り組んでおり、プロシージャ内で約 2 秒間少し一時停止したいと考えています。

実際に Invoke を使ってみたのですが、ご存じのとおり、クラス内でこの種の手続きを使用することはできません。

詳細については、私のコードを次に示します。

public class GenerateFile
{

    public CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
    {
        string script = string.Format(" DECLARE @RC INT " +
                                        " DECLARE @Daftar_No INT = '{0}' " +
                                        " DECLARE @hokm_type_code INT = 100 " +
                                        " DECLARE @Channelno INT = '{1}' " +
                                        " DECLARE @Id_No BIGINT = '{2}' " +
                                        " EXEC @rc = [dbo].[Hokm_with_type] @Daftar_No, @hokm_type_code, @Channelno, @Id_No ",
                                        Daftar_No,
                                        Channelno,
                                        NationalCode);
        try
        {
            IEnumerable<string> commandStrings = Regex.Split(script, @"^\s*GO\s*$",
                                                    RegexOptions.Multiline | RegexOptions.IgnoreCase);
            Connect();
            foreach (string commandString in commandStrings)
            {
                if (commandString.Trim() != "")
                {
                    using (var command = new SqlCommand(commandString, Connection))
                    {
                        command.ExecuteNonQuery();
                    }
                }
            }

            DisConnect();

            string FaxFilePath = InternalConstant.FaxFilePath + "\\" + string.Format("Lhokm{0}.tif", Channelno);


            // I want to make a pause in here without locking UI

            if (File.Exists(FaxFilePath))
                return CSPFEnumration.ProcedureResult.Success;
            else
                return CSPFEnumration.ProcedureResult.Error;

        }
        catch (Exception ex)
        {
            InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
            return CSPFEnumration.ProcedureResult.Error;
        }
    }
}

私も await を試しましたが、適切な値を返すことができません。この手順で await を使用すると、await を終了する前に値が返されるためです。

編集:

Thread.Sleepまた、 UIがロックされるため、使用したくありません。助けてくれてありがとう。

4

7 に答える 7

7

非同期待機機能を使用します。

メソッドを async としてマークします。

待ちタスクとして Task.Delay(2000) を追加します。

 public async CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
        {
                -----
                // I want to make a pause in here without locking UI
              await Task.Delay(2000);
                -----
        }
于 2015-10-13T06:16:33.003 に答える
1

Task.Delay()現在のスレッドをブロックせず、数ミリ秒後に実行を継続するように見回すことができます。

msdn からの使用:

Stopwatch sw = Stopwatch.StartNew();
var delay = Task.Delay(1000).ContinueWith(_ =>
                           { sw.Stop();
                             return sw.ElapsedMilliseconds; } );

Console.WriteLine("Elapsed milliseconds: {0}", delay.Result);
// The example displays output like the following:
//        Elapsed milliseconds: 1013

あるいは、Timerクラスを見回してみてください。

于 2015-10-08T05:37:04.423 に答える
0

古い良いバックグラウンド スレッド (FabJan によって書かれた回答を参照) を使用するか、同期コンテキストで async と await を使用できます。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private async void buttonStart_Click(object sender, EventArgs e)
    {
        await progressBar1.DoProgress(2000);
        Trace.WriteLine("Done");          
        MessageBox.Show("Done");
    }

    private void buttonMoveButton1_Click(object sender, EventArgs e)
    {
        //to prove UI click several times buttonMove while the task is ruunning
        buttonStart.Top += 10;
    }
}

public static class WaitExtensions
{
    public static async Task DoProgress(this ProgressBar progressBar, int sleepTimeMiliseconds)
    {
        int sleepInterval = 50;
        int progressSteps = sleepTimeMiliseconds / sleepInterval; //every 50ms feedback
        progressBar.Maximum = progressSteps;

        SynchronizationContext synchronizationContext = SynchronizationContext.Current;
        await Task.Run(() =>
        {
            synchronizationContext.OperationStarted();

            for (int i = 0; i <= progressSteps; i++)
            {
                Thread.Sleep(sleepInterval);
                synchronizationContext.Post(new SendOrPostCallback(o =>
                {
                    Trace.WriteLine((int)o + "%");

                    progressBar.Value = (int)o;
                }), i);
            }

            synchronizationContext.OperationCompleted();
        });
    }
}    

ProgressBar が最大値になる前に MessageBox done Shows が表示されることがあります。Windows 8 のプログレス バーのこの魔法のアニメーションのせいです。間違っていたら訂正してください。

于 2015-10-19T20:40:17.083 に答える
0

UI の応答性を維持しながら待機する最も簡単な方法は、async-await を使用することです。

これを行うには、関数 async を宣言し、void の代わりに Task を返し、TResult の代わりに Task <TResult>を返す必要があります。

public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
    string Daftar_No,
    string Channelno,
    string NationalCode)
{
    // do your stuff,
}

時間がかかることをするときはいつでも、関数の非同期バージョンを使用してプロセスを開始します。このプロセスが実行されている間、他のことを行うことができます。タスクの結果を待機する必要があり、非同期が Task を返す場合は void を取得し、非同期が Task <TResult>を返す場合は TResult を取得します。

public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
    string Daftar_No,
    string Channelno,
    string NationalCode)
{
    IEnumerable<string> commandStrings = Regex.Split(
        script, @"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);

    Connect();
    foreach (var commandString in commandStrings)
     {
        if (commandString.Trim() != "")
        {
            using (var command = new SqlCommand(commandString, Connection))
                {
                    Task<int> task = command.ExecuteNonQueryAsync();
                    // while the command is being executed
                    // you can do other things.
                    // when you need the result: await
                    int result = await task;
                    // if useful: interpret result;
                }
            }
        }

        DisConnect();
        ... etc.
}
  • 非同期関数を呼び出すすべての関数は、非同期として宣言する必要があります
  • <TResultすべての非同期関数は、void の代わりに Task を返し、TResult の代わりに Task > を返します。
    • 例外は 1 つだけです。イベント ハンドラが void を返す場合があります。

非同期イベント ハンドラーの例:

private async void OnButton1_Clicked(object sender, ...)
{
    var task = GenerateFaxFile(...);
    // while the fax file is generated do some other stuff
    // when you need the result:
    var procedureResult = await task;
    Process(procedureResult);
}

すべてが UI スレッドによって処理されることに注意してください。唯一の違いは、時間がかかる何かが発生するとすぐに、プロセスがビジー状態で待機するのではなく、UI 入力を処理することです。

上記は、UI の応答性を維持するのに十分です。あなたは、しばらく待つ方法を知りたいと言った。あなたの質問の残りの部分から、UI が他のことをできるように、何かを待っている間に手順を中断する方法を意味していることを理解しています。UI の応答性を維持するためにしばらく待機する必要がある場合は、Task.Delay(TimeSpan) を使用します。

Eric Lippert (thanx Eric!) は async-await を Stackoverflow で次のように説明しました - async/await - Is this Understanding correct?

朝食にパンをトーストし、卵を調理する必要があるとします。これにはいくつかのシナリオがあります。

  1. パンを焼き始めます。完了するまで待ちます。卵の調理を開始し、完成するまで待ちます。同期処理。パンが焼けるのを待っている間は、他に何もできません。
  2. パンをトーストし始め、パンをトーストしている間に卵の調理を開始します。卵が調理されたら、パンのトーストが終わるまで待ちます。これは非同期と呼ばれますが、同時ではありません。これはメイン スレッドによって行われ、このスレッドが何かを行う限り、メイン スレッドは他に何もできません。しかし、待っている間、他のことをする時間があります (たとえば、お茶を作るなど)。
  3. 料理人を雇ってパンを焼いたり、卵を調理したりします。両方が完了するまで待ちます。非同期および並行: 作業は異なるスレッドによって行われます。これは、新しいスレッドを開始する必要があるため、最もコストがかかります。

最後に、例外処理に関するメモ

例外が発生した場合、切断しないことに気付きましたか?. 切断が常に呼び出されるようにする適切な方法は次のとおりです。

try
{
    Connect();
    ... do other stuff
}
catch (Exception exc)
{
     ... process exception
}
finally
{
    Disconnect();
}

最終部分は、例外がスローされるかどうかに関係なく、常に実行されます。

于 2015-10-13T08:22:29.493 に答える
0

シンプルなスレッド プールを使用してこれをアーカイブできます。ただし、リターンは非同期で行う必要があるため、GUI がロックアップすることはありません。

public void GenerateFaxFile(string Daftar_No, string Channelno,
            string NationalCode, Action<CSPFEnumration.ProcedureResult> result)
        {
            ThreadPool.QueueUserWorkItem(o =>
            {
                string script = "your script";
                try
                {
                    // more of your script

                    // I want to make a pause in here without locking UI
                    while (true)
                    {
                        // do your check here to unpause
                        if (stopMe == true)
                        {
                            break;
                        }

                        Thread.Sleep(500);
                    }


                    if (File.Exists(FaxFilePath))
                    {
                        result(CSPFEnumration.ProcedureResult.Success);
                        return;
                    }
                    else
                    {
                        result(CSPFEnumration.ProcedureResult.Error);
                    }


                }
                catch (Exception ex)
                {
                    InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
                    result(CSPFEnumration.ProcedureResult.Error);
                    return;
                }

            });

        }

        public void HowToUseMe()
        {
            GenerateFaxFile("", "", "", result => {

                if (result == CSPFEnumration.ProcedureResult.Error)
                {
                    // no good
                }
                else
                {
                    // bonus time
                }
            });
        }
于 2015-10-19T20:33:51.160 に答える