0

私はC#と並列プログラミングにかなり慣れていません。次のバックグラウンドタスクがあります。

IsBusy = true;
Task.Factory.StartNew(() =>
    {
        reportService.CreateReport();
    }).
ContinueWith(task =>
    {
        IsBusy = false;
    },
TaskScheduler.FromCurrentSynchronizationContext());

現在、ReportServiceのCreateReportは次のようになっています。

private volatile Report report; 

public Report { get { return report; } }

CreateReport() 
{
   lock(this) {
       do some work computing result

       report = result
       RaisePropertyChanged(() => Report)
   }
}

RaisePropetyChangedPropertyChangedUIスレッドのコンテキストでイベントを発生させる必要があります。ただし、ReportServiceはバックグラウンドでの実行を認識していないはずです。ReportServiceがバックグラウンドで実行されていることを検出するための洗練された手段はありますか?それはPropertyChangedEventをUIスレッドにマッシュオールする必要がありますか?このマーシャリングはどのように実装されますか?利用できApplication.Context.Dispatcherますか?

4

3 に答える 3

0

UIスレッドで継続を実行しようとしていると思います。その場合、reportServiceクラスにNotifyというメソッドを作成することはできません。

Notify(){RaisePropertyChanged(()=>レポート); }

続きからそれを呼び出します。このようにして、UIスレッドで実行されます。

.ContinueWith(task =>
{
    IsBusy = false;
    reportService.Notify();
}, TaskScheduler.FromCurrentSynchronizationContext());
于 2013-03-26T17:09:02.910 に答える
0

あなたの質問に対する私の直接の答えは、を利用することですがDispatcher、それはあなたのソリューションをUIフレームワークに固有のものにしませんか?

また、フローを少し再設計することをお勧めします。ルーチンの長期実行の側面はCreateReport、ReportServiceの内部詳細です。次のように、そのクラス内でマルチスレッド/非同期の詳細を移動してみませんか。

public class ReportService
    {
        public event EventHandler NotifyReportGenerated;

        public Task CreateReportAsync()
        {
            return Task.Factory.StartNew(() =>
                {
                    GenrateActualReport();
                }).
            ContinueWith(task =>
                {
                    if (NotifyReportGenerated != null)
                        NotifyReportGenerated(this, new EventArgs());
                },
            TaskScheduler.FromCurrentSynchronizationContext());
        }

        private void GenrateActualReport()
        {
            var a = Task.Delay(10000);
            Task.WaitAll(a);
        }
    }

私が行った簡単なテストは、UIを満足させているようです。これは、私がWindowsフォームソリューションでこれを「消費」した方法です。

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

        private ReportService rpt = new ReportService();

        private void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            rpt.NotifyReportGenerated += rpt_NotifyReportGenerated;
            rpt.CreateReportAsync();
        }

        void rpt_NotifyReportGenerated(object sender, EventArgs e)
        {
            rpt.NotifyReportGenerated -= rpt_NotifyReportGenerated;
            button1.Enabled = true;
        }
    }
于 2013-03-26T19:20:50.440 に答える
0

貴重なご意見をありがとうございました。現在の解決策は次のとおりです。DispatcherService通知する必要があるすべてのクラスの基本クラスに注入されるを実装しましたPropertyChanged

public class WPFUIDispatcherService : IDispatcherService
{
    public void Invoke(Action action)
    {
        // check if the calling thread is the ui thread 
        if (Application.Current.Dispatcher.CheckAccess())
        {
            // current thread is ui thread -> directly fire the event
            action.DynamicInvoke();
        }
        else
        {
            // current thread is not ui thread so marshall 
            // the event to the ui thread
            Application.Current.Dispatcher.Invoke(action);
        }
    }
}

NotifyPropertyChangedBaseキーラインはdispatcher.Invoke( .. )

public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    private IDispatcherService dispatcher;

    // inject dispatcher service by unity
    [Dependency]
    public IDispatcherService Dispatcher { set { dispatcher = value; } }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged<T>(
                          Expression<Func<T>> propertyExpression
                   )
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            MemberExpression memberExpression = 
                    propertyExpression.Body as MemberExpression;
            if (memberExpression != null)
            {
                dispatcher.Invoke(() => 
                    handler(this, new PropertyChangedEventArgs(
                                            memberExpression.Member.Name
                                      )
                           )
                );
            }
            else
            {
                throw new ArgumentException(
                     "RaisePropertyChanged event " +
                     "was not raised with a property: " + 
                     propertyExpression);
            }
        }
    }
}

ReportService:_

public class ReportService : NotifyPropertyChangedBase, IReportService
{ 
    private volatile Report report; 

    public Report { get { return report; } }

    CreateReport() 
    {
       lock(this) {
           do some work computing result

           report = result
           RaisePropertyChanged(() => Report)
    }
}

私のサービスの呼び出し

IsBusy = true;
Task.Factory.StartNew(() =>
    {
        reportService.CreateReport();
    }).
ContinueWith(task =>
    {
        IsBusy = false;
    },
TaskScheduler.FromCurrentSynchronizationContext());

ReportServiceアプリケーションは設計されていますが、バックグラウンドタスクまたはフォアグラウンドで透過的に実行できるようになりました。いずれの場合も、UIスレッド内でイベントが発生することRaisePropertyChangedを保証します。WPFUIDispatcherService

于 2013-03-27T08:57:55.690 に答える