none
Thread.Abord()に関する質問 RRS feed

  • 質問

  • 初めまして、

    現在スレッドに関する知識を身に付ける、練習PJを作っております。

    概要は、カードをかざして、カードIDを取って、サーバに問合せます。 取り合わせるたびに、ぐるぐる回るWaiting画面を表示しております。

    ですが、現在、サーバへの取り合わせが高頻度で行うと、アプリケーションが異常終了となる現象がありました。

    Try...Catchでキャッチしたいですが、うまくいけませんでした。

    ソースコードが下記のとおりです。

    ----------------------------------------------

     void DispatcherTimer_Tick(object sender, EventArgs e)
            {
                string cardID = ListenPCSC();
                if (!string.IsNullOrWhiteSpace(cardID))
                {
                    var userinfo =GetUserInfoByCardId(cardID);
                    // 成功したかどうか
                    if (userinfo.returnCode != 0)
                    {

                      // エラー処理
                    }
                    // 成功したら、当画面を閉じる
                    DialogResult = true;                
                }
            }

    -----------------------------------------------------

    GetUserInfoByCardId の中に下記の関数をよって、サーバに送信しております。
    public static  HTTPResult DoPost(Dictionary<string, string> RequestContent)
            {
                // 通信するたびにWaiting画面を表示させる
                TMSSplashScreen splashScreen = new TMSSplashScreen();
                splashScreen.ShowSplashScreen();
                // サーバ通信と結果返却
                // 通信終了したら、Waiting画面を閉じる
                splashScreen.CloseSplashScreen();
    }

    -----------------------------------

     public void ShowSplashScreen()
            {            
                _splashScreenThread = new Thread(new ThreadStart(ShowScreen));
                _splashScreenThread.IsBackground = true;
                _splashScreenThread.SetApartmentState(ApartmentState.STA);
                _splashScreenThread.Start();          
            }
            
            public void ShowScreen()
            {
                try
                {
                    _splashScreenView = new LoadingWaitView();
                    _splashScreenView.ShowDialog();
                }
                catch (Exception ex)
                {
                    logger.Info(" ShowScreen:" + ex.StackTrace);
                }   
                
            }


            /// <summary>
            /// Closes the SplashScreen
            /// </summary>
            public void CloseSplashScreen()
            {
                if (_splashScreenView != null)            
                {
                    _splashScreenView.Dispatcher.Invoke(() =>
                    {
                        _splashScreenView.Close();                    
                        _splashScreenView = null;                    
                    });
                }
                
                logger.Info(" waiting end 1:" + _splashScreenThread.ThreadState.ToString() + "  islive:" + _splashScreenThread.IsAlive.ToString());
                if (_splashScreenThread.IsAlive)
                {
                    try
                    {
                        _splashScreenThread.Abort();
                        _splashScreenThread.Join();
                    }
                    catch (ThreadAbortException ex)
                    {
                        logger.Info(" ThreadAbortException:" + ex.StackTrace);
                        try { Thread.ResetAbort(); }
                        catch (Exception)
                        {

                        }
                        logger.Info(" waiting end 2:" + _splashScreenThread.ThreadState.ToString() + "  islive:" + _splashScreenThread.IsAlive.ToString());
                    }
                    catch (Exception e)
                    {
                        logger.Info(" waiting end 3:" + _splashScreenThread.ThreadState.ToString() + "  islive:" + _splashScreenThread.IsAlive.ToString());
                        logger.Info(" Exception:" + e.StackTrace);
                        try { Thread.ResetAbort(); }
                        catch(Exception)
                        {

                        }
                        
                    }

                }
                logger.Info(" waiting end 4:" + _splashScreenThread.ThreadState.ToString() + "  islive:" + _splashScreenThread.IsAlive.ToString());
                _splashScreenThread = null;            
            }

    ログには Abortエラーがポコポコで出てきていますが、それでも無事にうごいてます。

    アプリケーションエラーになったときは、Try...Catchできません。

    ログには下記のようになっています。

    016-03-05 13:37:08,021[1] INFO -  waiting end 1:Background  islive:True
    2016-03-05 13:37:08,022[100] INFO -  ShowScreen:   場所 System.Threading.Monitor.Enter(Object obj)
       場所 System.Windows.Baml2006.Baml2006SchemaContext.TryGetBamlProperty(Int16 propertyId, BamlProperty& bamlProperty, XamlMember& xamlMember)
       場所 System.Windows.Baml2006.Baml2006SchemaContext.GetProperty(Int16 propertyId, XamlType parentType)
       場所 System.Windows.Baml2006.Baml2006Reader.Process_PropertyWithConverter()
       場所 System.Windows.Baml2006.Baml2006Reader.Process_OneBamlRecord()
       場所 System.Windows.Baml2006.Baml2006Reader.Process_BamlRecords()
       場所 System.Windows.Baml2006.Baml2006Reader.Read()
       場所 System.Windows.Markup.WpfXamlLoader.TransformNodes(XamlReader xamlReader, XamlObjectWriter xamlWriter, Boolean onlyLoadOneNode, Boolean skipJournaledProperties, Boolean shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, XamlContextStack`1 stack, IStyleConnector styleConnector)
       場所 System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
       場所 System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
       場所 System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
       場所 System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
       場所 TMS.Common.TMSSplashScreen.ShowScreen() 場所 f:\Project\TMS\Source\TMS\TMS\Common\SplashScreen.cs:行 47
    2016-03-05 13:37:08,032[1] INFO -  waiting end 4:Aborted  islive:False

    .........

    2016-03-05 13:37:08,989[1] INFO - ユーザ情報取得 Start
    2016-03-05 13:37:08,989[1] INFO - Send :{apitype=reserve,command=getuserinfo,id=01140214D314C90E}
    2016-03-05 13:37:08,993[1] INFO -  waiting end 1:Background  islive:True---ログがここまで、アプリケーション異常終了

    ----------------------------------------------

    上記のソースに何か問題があったでしょうか。どうのように直せばいいのでしょうか。

    皆さまのご指導をお承りいただけますか?

    2016年3月5日 5:53

回答

  • メインスレッドで待機画面を表示し、バックグラウンドスレッドでサーバー問い合わせを行い、問い合わせ完了後に再びメインスレッドで待機画面を閉じればよいと思います。

    また、スレッドの管理が難しければasync / await 構文を使ってみてもいいと思います。

    適当に打ち込みましたが、以下のような感じが単純だと思いますが。

    async void Hoge()
    {
        //待機画面表示
        Screen.Show();
    
        //サーバー通信
        await Task.Run(() => 
        {
            Server.Send();
        });
    
        //待機画面閉じる
        Screen.Close();
    }
    上記例外処理が入ってませんが、このメソッド内で例外処理にて確実に待機画面が閉じるようには出来ると思います。



    2016年3月5日 7:08
    モデレータ

すべての返信

  • こんにちは。

    メインスレッドではなく、バックグラウンドスレッドで待機画面を表示させようとしてますか?
    あと、ログ出力時はスタックトレースだけではなく、ex.ToString()でメッセージなど含めてまとめて出力したほうが情報としては良い気がします。
    プラットフォームはWindowsFormsでしょうか、WPFでしょうか。

    2016年3月5日 6:36
    モデレータ
  • ご回答ありがとうございます。

    環境は VS2013 + .net 4.5 + WPF です。

    現在の呼び方としては下記のようになります。

    ①Main画面にあるボタンを押したら、カード認証画面が表示する

    ②カード認証画面の構造関数にDispatcherTimerの初期化とStart()などを設定しいます。

    ③DispatcherTimerのイベンドにカードIDを取得し、サーバに問合せします。ListenPCSC:パソリからカードIDをとるための関数

    GetUserInfoByCardId:サーバに問合せる用関数

    ④GetUserInfoByCardIdの中にWaiting画面を別のスレッドで表示しています。

    ⑤サーバとの通信が終わってから、Waiting画面をクローズします。

    ⑥上記の⑤で、画面がクローズされましたが、スレッドがまだ生きています。

    Waiting画面がまだうごいています。

    もし、Abortを呼ばなかったら、何のエラーも発生しないですが、Waiting画面がうまくクローズされる場合だと、

    通信が終わってもずっと表示しています。

    もし、この方法がダメだったら、ほかの解決方法を教えていただければ、ありがたいです。

    2016年3月5日 7:00
  • メインスレッドで待機画面を表示し、バックグラウンドスレッドでサーバー問い合わせを行い、問い合わせ完了後に再びメインスレッドで待機画面を閉じればよいと思います。

    また、スレッドの管理が難しければasync / await 構文を使ってみてもいいと思います。

    適当に打ち込みましたが、以下のような感じが単純だと思いますが。

    async void Hoge()
    {
        //待機画面表示
        Screen.Show();
    
        //サーバー通信
        await Task.Run(() => 
        {
            Server.Send();
        });
    
        //待機画面閉じる
        Screen.Close();
    }
    上記例外処理が入ってませんが、このメソッド内で例外処理にて確実に待機画面が閉じるようには出来ると思います。



    2016年3月5日 7:08
    モデレータ
  • ご回答ありがとうございます。上記のように改修してみます。
    2016年3月5日 7:28
  • メインスレッドで待機画面を表示し、バックグラウンドスレッドでサーバー問い合わせを行い、問い合わせ完了後に再びメインスレッドで待機画面を閉じればよいと思います。

    また、スレッドの管理が難しければasync / await 構文を使ってみてもいいと思います。

    適当に打ち込みましたが、以下のような感じが単純だと思いますが。

    async void Hoge()
    {
        //待機画面表示
        Screen.Show();
    
        //サーバー通信
        await Task.Run(() => 
        {
            Server.Send();
        });
    
        //待機画面閉じる
        Screen.Close();
    }
    上記例外処理が入ってませんが、このメソッド内で例外処理にて確実に待機画面が閉じるようには出来ると思います。



    返事が遅くなって申し訳ありません。上記のように直してみたら、1回もエラーが発生しませんでした。

    どうもありがとうございました。

    • 回答としてマーク icyfire028 2016年3月19日 7:01
    • 回答としてマークされていない icyfire028 2016年3月19日 7:07
    2016年3月19日 7:01