C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[5]: 非同期コールバックについて


(過去ログ 10 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■1728 / inTopicNo.1)  非同期コールバックについて
  
□投稿者/ Hirotow (48回)-(2007/03/04(Sun) 22:06:46)
Hirotow さんの Web サイト

分類:[C# (Windows)] 

次のようなコード

// using ディレクティブ省略

namespace Application
{
    class DownloadItem
    {
        // フィールド・プロパティ宣言省略。

        /// <summary>
        /// ダウンロードを開始します。
        /// </summary>
        /// <param name="resume">リジュームを試行する場合は true、さもなければ false。</param>
        public void Start(bool resume)
        {
            //==============================
            // 状態更新
            //==============================
            MyState = DownloadState.Download;

            //==============================
            // 初期化
            //==============================
            MyTotalBytes = 0;
            MyRecievedBytes = 0;
            MyRecieveSpeed = 0;

            MyStopWatch.Reset();

            CallStatusChanged();

            //==============================
            // リクエスト生成
            //==============================
            try
            {
                MyRequest = (HttpWebRequest)WebRequest.Create(MyUrl);
            }
            catch
            {
                MyState = DownloadState.Failure;
                CallStatusChanged();

                return;
            }

            //==============================
            // リジュームフィールド追加
            //==============================
            if (resume)
            {
                FileInfo locinfo = new FileInfo(MyLocation);

                if (locinfo.Exists)
                {
                    long length = locinfo.Length;
                    if (length > 0)
                    {
                        MyRequest.AddRange((int)length);
                        string etag = GetETag();
                        if (etag != null)
                            MyRequest.Headers.Add("If-Range", etag);
                    }
                }
            }

            //==============================
            // 全般フィールド追加
            //==============================
            MyRequest.KeepAlive = false;
            MyRequest.Headers.Add("Pragma", "no-cache");
            MyRequest.Headers.Add("Cache-Control", "no-cache");

            //==============================
            // リクエスト送信
            //==============================
            MyRequest.BeginGetResponse(new AsyncCallback(GetResponseCallback), null);
        }

        private void GetResponseCallback(IAsyncResult ar)
        {
            MyResponse = null;

            //==============================
            // レスポンス取得
            //==============================
            try
            {
                MyResponse = (HttpWebResponse)MyRequest.EndGetResponse(ar);
            }
            catch
            {
                MyState = DownloadState.Failure;
                CallStatusChanged();

                return;
            }

            //==============================
            // エンティティタグ保存
            //==============================
            string etag = MyResponse.GetResponseHeader("ETag");
            if (!string.IsNullOrEmpty(etag))
                SetETag(etag);

            //==============================
            // ストリーム取得
            //==============================
            MyGetStream = MyResponse.GetResponseStream();
            MyOutStream = File.Open(MyLocation, FileMode.OpenOrCreate, FileAccess.Write);

            //==============================
            // 保存ストリームシーク処理
            //==============================
            long pos = 0;

            if (MyResponse.StatusCode == HttpStatusCode.PartialContent)
            {
                string crstr = MyResponse.GetResponseHeader("Content-Range");
                Match rm = Regex.Match(crstr, @"bytes\s+(?:(?<first>\d*)-(?<last>\d*)|\*)/(?:(?<len>\d+)|\*)");
                if (int.TryParse(rm.Groups["first"].Value, out pos))
                    pos = first;
                else
                    pos = 0;

            }

            MyOutStream.SetLength(pos);
            MyOutStream.Position = pos;

            //==============================
            // 全体バイト数取得
            //==============================
            MyTotalBytes = MyResponse.ContentLength;

            //==============================
            // 受信開始
            //==============================
            MyBuffer = new byte[4096];
            MyGetStream.BeginRead(MyBuffer, 0, MyBuffer.Length, new AsyncCallback(BeginReadCallBack), null);

            MyStopWatch.Start();
        }

        private void BeginReadCallBack(IAsyncResult result)
        {
            int len = MyGetStream.EndRead(result);

            //==============================
            // 受信終了
            //==============================
            if (len == 0 || MyCancelationPending)
            {
                MyCancelationPending = false;

                MyOutStream.Close();
                MyGetStream.Close();

                if (MyResponse.ContentLength > 1000000)
                    MyState = DownloadState.Complete;
                else
                    MyState = DownloadState.Failure;

                CallStatusChanged();

                return;
            }

            //==============================
            // 受信
            //==============================
            MyOutStream.Write(MyBuffer, 0, len);

            //==============================
            // 状態更新
            //==============================
            MyRecievedBytes += len;
            MyRecievedParcentage = (double)MyRecievedBytes / (double)MyTotalBytes;
            double ms = MyStopWatch.Elapsed.TotalSeconds;
            MyRecieveSpeed = (long)((double)len / (ms - MyLastRecieved));
            MyLastRecieved = ms;

            CallStatusChanged();
        }

        /// <summary>
        /// 保存されているエンティティタグからこのダウンロードに一致するものを取得します。
        /// </summary>
        /// <returns>一致したエンティティタグ、または null。</returns>
        private string GetETag() //省略

        /// <summary>
        /// 受信したエンティティタグを保存します。
        /// </summary>
        /// <param name="etag">エンティティタグ</param>
        /// <returns>エンティティタグが上書きされた場合は true、新規登録された場合は false。</returns>
        private bool SetETag(string etag) //省略

        /// <summary>
        /// ダウンロードを停止します。
        /// このメソッドの実行には数秒かかることがあります。
        /// </summary>
        /// <returns>現在ダウンロードが実行されていなければ true、さもなければ false。</returns>
        public bool Cancel()
        {
            if (MyState == DownloadState.Download)
            {
                if (MessageBox.Show(string.Format("{0} のダウンロードを停止しますか?", Title), "中断確認", MessageBoxButtons.YesNo) == DialogResult.No)
                    return false;

                MyCancelationPending = true;
                while (MyState == DownloadState.Download) ;
            }

            CallStatusChanged();

            return true;
        }

        public bool StandBy()
        {
            if (Cancel())
            {
                MyState = DownloadState.StandBy;
                CallStatusChanged();
            }
        }

        public bool Suspend()
        {
            if (Cancel())
            {
                MyState = DownloadState.Suspend;
                CallStatusChanged();
            }
        }

        // 以下省略
    }
}

のCancel()メソッドが怖いのですが、安全かつ確実に処理を停止させるにはマルチスレッドで実行したほうがよいのでしょうか?
どなたかご教授願います。

引用返信 編集キー/
■1730 / inTopicNo.2)  Re[1]: 非同期コールバックについて
□投稿者/ シャノン (95回)-(2007/03/05(Mon) 09:15:40)
No1728 (Hirotow さん) に返信
> Cancel()メソッドが怖いのですが、安全かつ確実に処理を停止させるにはマルチスレッドで実行したほうがよいのでしょうか?
> どなたかご教授願います。

怖いとはどのように?
引用返信 編集キー/
■1735 / inTopicNo.3)  Re[2]: 非同期コールバックについて
□投稿者/ Hirotow (50回)-(2007/03/05(Mon) 10:26:34)
Hirotow さんの Web サイト
まずCallback()メソッドはマルチスレッドで実行されているのかなということ。
それと、同期処理のStart()メソッドの実行中にCancel()された場合デッドロックが発生してしまうことです。
タイムアウトは別途実装するので大丈夫です。
引用返信 編集キー/
■1750 / inTopicNo.4)  Re[3]: 非同期コールバックについて
□投稿者/ シャノン (97回)-(2007/03/05(Mon) 15:46:20)
No1735 (Hirotow さん) に返信
> まずCallback()メソッドはマルチスレッドで実行されているのかなということ。

Yes。

> それと、同期処理のStart()メソッドの実行中にCancel()された場合デッドロックが発生してしまうことです。

lock で避けられないかな。
引用返信 編集キー/
■1751 / inTopicNo.5)  Re[4]: 非同期コールバックについて
□投稿者/ シャノン (98回)-(2007/03/05(Mon) 15:47:09)
No1750 (シャノン さん) に返信
> ■No1735 (Hirotow さん) に返信
>>まずCallback()メソッドはマルチスレッドで実行されているのかなということ。
>
> Yes。
>
>>それと、同期処理のStart()メソッドの実行中にCancel()された場合デッドロックが発生してしまうことです。
>
> lock で避けられないかな。

というか、このクラスを呼び出す側はマルチスレッドなのね。
引用返信 編集キー/
■1753 / inTopicNo.6)  Re[5]: 非同期コールバックについて
□投稿者/ Hirotow (52回)-(2007/03/05(Mon) 16:12:43)
Hirotow さんの Web サイト
> というか、このクラスを呼び出す側はマルチスレッドなのね。
そういう実装もあるのか(汗)

とりあえず非同期のままBackgroundWorkerを使うようにしました。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -