什么是更新从任务工厂派出一个线程在WPF UI中的正确方法是什么?线程、工厂、正确、任务

2023-09-08 09:43:33 作者:自定义旋律

我想更新从我的线程中的一个通过 Task.Factory 调度的UI 我已经有很难正确地更新我的UI。

下面是我观察的行为:

  Task.Factory.StartNew(()=>
    {
        // UI不会得到在这里更新。
    })ContinueWith(任务=>
        {
            // UI并*不*获得从这里更新。
        });
 

能否请你帮我在这里? 什么是正确的方法来更新UI withing派遣与线程任务工厂

WPF 如何在工作线程中更新窗体的UI元素 Dispatcher机制

下面是我的实际code,供大家参考:

================================

 私人字符串CurrentProcess
{
    集合{_eventAggregator.GetEvent< CurrentProcessUpdatedEvent>()发布(值)。 }
}

私人双人ProgressPercentage
{
    组
    {
        _eventAggregator.GetEvent< ProgressPercentageUpdatedEvent>()
                        .Publish(Utilities.GetProgressPercentage(值));
    }
}
 

================================

 的TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
VAR任务=新的名单,其中,任务<数据表>>();

串siteCollectionUrl;
字符串的connectionString;

尝试
{
    字典<字符串,对象>会话= ApplicationContext.Current.Session;

    尝试
    {
        如果((双)会话[ProgressPercentage] 0)回报;
    }
    抓住
    {
    }

    siteCollectionUrl =(字符串)会议[SiteCollection];
    的connectionString =(字符串)会议[数据库];
}
抓住
{
    返回;
}

_eventAggregator.GetEvent< IsProcessingChangedEvent>()发布(真)。
CurrentProcess =加载资源。

任务<数据表> spTask =任务<数据表> .Factory.StartNew(()=>
    {
        使用(的ChannelFactory< ISharePointService>服务= Utilities.GetSharePointService())
        {
            ISharePointService sharePointService = service.CreateChannel();
            数据表spDatatable = sharePointService.GetResources(siteCollectionUrl);

            Task.Factory.StartNew(()=> {ProgressPercentage = 10;},CancellationToken.None,TaskCreationOptions.None,uiScheduler);

            返回spDatatable;
        }
    });

tasks.Add(spTask);

任务<数据表> buildTableTask =任务<数据表> .Factory.ContinueWhenAll(tasks.ToArray(),T =>
    {
        数据表spDatatable = T [0]。结果;

        双百分比= 10 / spDatatable.Rows.Count;

        VAR columnMap =新字典<字符串,字符串>
            {
                {IsValid的空},
                {理,空},
                {SPID,ID},
                {DBID,EXTID},
                {姓名,标题},
                {账户,SharePointAccount},
                {电子邮件,电子邮件},
                {通用,一般},
                {部门,部门},
                {TempDept,TempDept},
                {角色,角色},
                {TempRole,TempRole},
                {HolidaySchedule,HolidaySchedule},
                {WorkHours,WorkHours}
            };

        数据表小号presources = BuildDataTable(columnMap);

        的foreach(DataRow的数据行的spDatatable.Rows)
        {
            行的DataRow = S presources.NewRow();

            的foreach(VAR对的columnMap)
            {
                尝试
                {
                    行[pair.Key] =数据行[pair.Value]
                }
                抓住
                {
                }
            }

            US presources.Rows.Add(行);

            Task.Factory.StartNew(()=> {ProgressPercentage =个百分点;},CancellationToken.None,TaskCreationOptions.None,uiScheduler);
        }

        返回小号presources;
    });

tasks.Add(buildTableTask);

任务<数据表> dbTask =任务<数据表> .Factory.StartNew(()=>
    {
        使用(VAR的SqlConnection =新的SqlConnection(的connectionString))
        {
            使用(VAR的SqlCommand =新的SqlCommand(SQL,SqlConnection的))
            {
                在SQLConnection.open();
                使用(SqlDataReader的SqlDataReader的= sqlCommand.ExecuteReader())
                {
                    VAR的dataTable =新的DataTable();
                    dataTable.Load(SqlDataReader的);

                    Task.Factory.StartNew(()=> {ProgressPercentage = 10;},CancellationToken.None,TaskCreationOptions.None,uiScheduler);

                    返回的dataTable;
                }
            }
        }
    });

tasks.Add(dbTask);

Task.Factory.ContinueWhenAll(tasks.ToArray(),T =>
    {
        DatabaseResources = T [2]。结果;
        数据表sharePointResources = T [1]。结果;

        如果(sharePointResources!= NULL)
        {
            INT resourceIndex = 1;
            INT totalResources = sharePointResources.Rows.Count;
            双percentPoint = 70 / totalResources;

            的foreach(DataRow的行sharePointResources.Rows)
            {
                DataRow的currentRow =行;

                Task.Factory.StartNew(()=>
                    {
                        CurrentProcess =的String.Format([{0} / {1}]处理:{2},
                                                        resourceIndex ++,totalResources,
                                                        currentRow [名称]);
                    },CancellationToken.None,TaskCreationOptions.None,uiScheduler);

                布尔的isValid = TRUE;
                VAR原因=新的名单,其中,串>();

                的DataRow []数据行=
                    _databaseResources.Select(的String.Format(的ResourceID = {0},行[DBID]));
                如果(dataRows.Any())
                {
                    DataRow的数据行=数据行[0];

                    字符串tempDept =(行[TempDept] ??的S​​tring.Empty)的ToString();
                    串部门=(行[部门] ??的S​​tring.Empty)的ToString();

                    字符串tempRole =(行[TempRole] ??的S​​tring.Empty)的ToString();
                    字符串的作用=(行[角色] ??的S​​tring.Empty)的ToString();

                    字符串HS =(行[HolidaySchedule] ??的S​​tring.Empty)的ToString();
                    字符串dbhs =(数据行[HolidaySchedule] ??的S​​tring.Empty)的ToString();

                    字符串WH =(行[WorkHours] ??的S​​tring.Empty)的ToString();
                    字符串dbwh =(数据行[WorkHours] ??的S​​tring.Empty)的ToString();

                    如果(string.IsNullOrEmpty(部门))
                    {
                        如果(!dept.Equals(tempDept))
                        {
                            的isValid = FALSE;
                            reasons.Add(系不匹配温度部);
                        }
                    }

                    如果(string.IsNullOrEmpty(角色))
                    {
                        如果(!role.Equals(tempRole))
                        {
                            的isValid = FALSE;
                            reasons.Add(角色不符温度作用);
                        }
                    }

                    如果(string.IsNullOrEmpty(HS))
                    {
                        如果(!hs.Equals(dbhs))
                        {
                            的isValid = FALSE;
                            reasons.Add(假日安排不匹配假日安排从数据库);
                        }
                    }

                    如果(string.IsNullOrEmpty(WH))
                    {
                        如果(!wh.Equals(dbwh))
                        {
                            的isValid = FALSE;
                            reasons.Add(工作时间不匹配数据库工作时间);
                        }
                    }
                }
                其他
                {
                    的isValid = FALSE;
                    reasons.Add(资源不存在于数据库);
                }

                行[的IsValid] =的isValid;
                行[理由] =的string.join(\ N,reasons.ToArray());

                Task.Factory.StartNew(()=> {ProgressPercentage = percentPoint;},CancellationToken.None,TaskCreationOptions.None,uiScheduler);
            }

            SharePointResources = sharePointResources;
        }

        _eventAggregator.GetEvent< ProgressPercentageUpdatedEvent>()
                        .Publish(Utilities.ResetProgressPercentage());
        _eventAggregator.GetEvent< IsProcessingChangedEvent>()发布(虚假的)。
    });
 

解决方案   

// UI不会得到这里更新

您应该推出一个新动作(()=> DispatcherObject的在WPF

  Task.Factory.StartNew(()=>
{
// UI不会得到在这里更新
        this.Dispatcher.BeginInvoke(新动作(()=>
        {
 

请搜索在第1部分 - 入门的亚历山德拉Rusina的系列在.NET Framework 4中并行编程

入门

我相信你一定会喜欢所有的续集从这个文献进一步描述。

第2部分 - 任务取消演示如何使用任务调度程序来代替:

  VAR UI = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
    结果=>
    {
        VAR时间= watch.ElapsedMilliseconds;
        label1.Content + = time.ToString();
    },CancellationToken.None,TaskContinuationOptions.None,UI);
 

来代替:

  Task.Factory.ContinueWhenAll(tasks.ToArray()
      结果=>
      {
          VAR时间= watch.ElapsedMilliseconds;
          this.Dispatcher.BeginInvoke(新动作(()=>
              label1.Content + = time.ToString()));
      });
 

在回应评论

  

首先,我使用的棱镜上。所以,在我ViewModwl我不得不使用   Dispatcher.Current.BeginInvoke ---我试过了。它并没有帮助

请检查答案是WPF调度的多线程问题的解决?与使用调度和访问UI在棱镜:

  //不是UI组件
公共类MyDomainService:IMyDomainService
{
   私人只读IDispatcher _dispatcher;

   公共MyDomainService(IDispatcher调度)
   {
      _dispatcher =调度;
   }

   私人无效GotResultFromBackgroundThread()
   {
       _dispatcher.Dispatch(()=> DoStuffOnForegroundThread());
   }
}
 

You需要确保你正在调用实际UI调度,不一定是当前

您可以从事的PRISM事件聚合,以确保您的UI线程或基本的 Dispatcher.CheckAccess方法

如果你使用的TaskScheduler,那么你应该得到 TaskScheduler.FromCurrentSynchronizationContext UI线程(例如,在Window.Loaded事件处理程序,你会得到双击窗体)和通/股/交任务。

I am trying to update the UI from one of my thread dispatched via Task.Factory I have having hard time properly updating my UI.

Here is the behavior I am observing:

Task.Factory.StartNew(() =>
    {
        // UI does get updated from here.
    }).ContinueWith(task =>
        {
            // UI does *not* get updated from here.
        });

Can you please help me out here? What is the proper way to update the UI withing a thread dispatched with Task Factory?

Here is my actual code for your reference:

================================

private string CurrentProcess
{
    set { _eventAggregator.GetEvent<CurrentProcessUpdatedEvent>().Publish(value); }
}

private double ProgressPercentage
{
    set
    {
        _eventAggregator.GetEvent<ProgressPercentageUpdatedEvent>()
                        .Publish(Utilities.GetProgressPercentage(value));
    }
}

================================

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var tasks = new List<Task<DataTable>>();

string siteCollectionUrl;
string connectionString;

try
{
    Dictionary<string, object> session = ApplicationContext.Current.Session;

    try
    {
        if ((double) session["ProgressPercentage"] > 0) return;
    }
    catch
    {
    }

    siteCollectionUrl = (string) session["SiteCollection"];
    connectionString = (string) session["Database"];
}
catch
{
    return;
}

_eventAggregator.GetEvent<IsProcessingChangedEvent>().Publish(true);
CurrentProcess = "Loading resources.";

Task<DataTable> spTask = Task<DataTable>.Factory.StartNew(() =>
    {
        using (ChannelFactory<ISharePointService> service = Utilities.GetSharePointService())
        {
            ISharePointService sharePointService = service.CreateChannel();
            DataTable spDatatable = sharePointService.GetResources(siteCollectionUrl);

            Task.Factory.StartNew(() => { ProgressPercentage = 10; }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

            return spDatatable;
        }
    });

tasks.Add(spTask);

Task<DataTable> buildTableTask = Task<DataTable>.Factory.ContinueWhenAll(tasks.ToArray(), t =>
    {
        DataTable spDatatable = t[0].Result;

        double percent = 10/spDatatable.Rows.Count;

        var columnMap = new Dictionary<string, string>
            {
                {"IsValid", null},
                {"Reason", null},
                {"SPID", "ID"},
                {"DBID", "EXTID"},
                {"Name", "Title"},
                {"Account", "SharePointAccount"},
                {"Email", "Email"},
                {"Generic", "Generic"},
                {"Department", "Department"},
                {"TempDept", "TempDept"},
                {"Role", "Role"},
                {"TempRole", "TempRole"},
                {"HolidaySchedule", "HolidaySchedule"},
                {"WorkHours", "WorkHours"}
            };

        DataTable spResources = BuildDataTable(columnMap);

        foreach (DataRow dataRow in spDatatable.Rows)
        {
            DataRow row = spResources.NewRow();

            foreach (var pair in columnMap)
            {
                try
                {
                    row[pair.Key] = dataRow[pair.Value];
                }
                catch
                {
                }
            }

            spResources.Rows.Add(row);

            Task.Factory.StartNew(() => { ProgressPercentage = percent; }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
        }

        return spResources;
    });

tasks.Add(buildTableTask);

Task<DataTable> dbTask = Task<DataTable>.Factory.StartNew(() =>
    {
        using (var sqlConnection = new SqlConnection(connectionString))
        {
            using (var sqlCommand = new SqlCommand(SQL, sqlConnection))
            {
                sqlConnection.Open();
                using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader())
                {
                    var dataTable = new DataTable();
                    dataTable.Load(sqlDataReader);

                    Task.Factory.StartNew(() => { ProgressPercentage = 10; }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

                    return dataTable;
                }
            }
        }
    });

tasks.Add(dbTask);

Task.Factory.ContinueWhenAll(tasks.ToArray(), t =>
    {
        DatabaseResources = t[2].Result;
        DataTable sharePointResources = t[1].Result;

        if (sharePointResources != null)
        {
            int resourceIndex = 1;
            int totalResources = sharePointResources.Rows.Count;
            double percentPoint = 70/totalResources;

            foreach (DataRow row in sharePointResources.Rows)
            {
                DataRow currentRow = row;

                Task.Factory.StartNew(() =>
                    {
                        CurrentProcess = string.Format("[{0}/{1}] Processing: {2}",
                                                        resourceIndex++, totalResources,
                                                        currentRow["Name"]);
                    }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

                bool isValid = true;
                var reasons = new List<string>();

                DataRow[] dataRows =
                    _databaseResources.Select(string.Format("ResourceID = {0}", row["DBID"]));
                if (dataRows.Any())
                {
                    DataRow dataRow = dataRows[0];

                    string tempDept = (row["TempDept"] ?? string.Empty).ToString();
                    string dept = (row["Department"] ?? string.Empty).ToString();

                    string tempRole = (row["TempRole"] ?? string.Empty).ToString();
                    string role = (row["Role"] ?? string.Empty).ToString();

                    string hs = (row["HolidaySchedule"] ?? string.Empty).ToString();
                    string dbhs = (dataRow["HolidaySchedule"] ?? string.Empty).ToString();

                    string wh = (row["WorkHours"] ?? string.Empty).ToString();
                    string dbwh = (dataRow["WorkHours"] ?? string.Empty).ToString();

                    if (string.IsNullOrEmpty(dept))
                    {
                        if (!dept.Equals(tempDept))
                        {
                            isValid = false;
                            reasons.Add("Department does not match Temp Dept");
                        }
                    }

                    if (string.IsNullOrEmpty(role))
                    {
                        if (!role.Equals(tempRole))
                        {
                            isValid = false;
                            reasons.Add("Role does not match Temp Role");
                        }
                    }

                    if (string.IsNullOrEmpty(hs))
                    {
                        if (!hs.Equals(dbhs))
                        {
                            isValid = false;
                            reasons.Add("Holiday Schedule does not match Holiday Schedule from database");
                        }
                    }

                    if (string.IsNullOrEmpty(wh))
                    {
                        if (!wh.Equals(dbwh))
                        {
                            isValid = false;
                            reasons.Add("Work Hours does not match Work Hours from database");
                        }
                    }
                }
                else
                {
                    isValid = false;
                    reasons.Add("Resource does not exist in database");
                }

                row["IsValid"] = isValid;
                row["Reason"] = string.Join("\n", reasons.ToArray());

                Task.Factory.StartNew(() => { ProgressPercentage = percentPoint; }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
            }

            SharePointResources = sharePointResources;
        }

        _eventAggregator.GetEvent<ProgressPercentageUpdatedEvent>()
                        .Publish(Utilities.ResetProgressPercentage());
        _eventAggregator.GetEvent<IsProcessingChangedEvent>().Publish(false);
    });

解决方案

// UI does get updated from here

You should launch a new Action(() => through DispatcherObject in WPF

Task.Factory.StartNew(() =>
    {
        // UI does get updated from here
        this.Dispatcher.BeginInvoke(new Action(() => 
        {

Please search for the last line in "Part 1 - Getting Started" of Alexandra Rusina's series "Parallel Programming in .NET Framework 4"

I am sure you will enjoy all sequel from this ref further on.

Part 2- Task Cancellation demonstrates how to use task scheduler instead:

var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
    result =>
    {
        var time = watch.ElapsedMilliseconds;
        label1.Content += time.ToString();
    }, CancellationToken.None, TaskContinuationOptions.None, ui);

instead of:

Task.Factory.ContinueWhenAll(tasks.ToArray(),
      result =>
      {
          var time = watch.ElapsedMilliseconds;
          this.Dispatcher.BeginInvoke(new Action(() =>
              label1.Content += time.ToString()));
      });  

In response to comments

"First of all, I am using PRISM. So, in my ViewModwl I have to use Dispatcher.Current.BeginInvoke --- I tried that. It did not help"

Please check the answer to "Is WPF Dispatcher the solution of multi threading problems?" related to use of dispatcher and accessing UI in Prism:

// Not a UI component
public class MyDomainService : IMyDomainService
{
   private readonly IDispatcher _dispatcher;

   public MyDomainService(IDispatcher dispatcher) 
   {
      _dispatcher = dispatcher;
   }

   private void GotResultFromBackgroundThread()
   {
       _dispatcher.Dispatch(() => DoStuffOnForegroundThread());
   }
}

"You need to make sure you are invoking on the actual UI Dispatcher, not necessarily the Current"

You can engage the PRISM event aggregator to ensure that you are on the UI thread or basic Dispatcher.CheckAccess Method

If you use TaskScheduler, then you should get TaskScheduler.FromCurrentSynchronizationContext on UI thread (for example, in Window.Loaded event handler, you will get on double clicking your form) and pass/share to/with tasks.

 
精彩推荐
图片推荐