【问题标题】:Thread access control on resource after Dispatcher.Invoke not workingDispatcher.Invoke 后资源的线程访问控制不起作用
【发布时间】:2022-08-16 22:53:14
【问题描述】:

我正在努力解决与阻止通过同一线程(UI 线程)访问资源相关的问题。这是场景。用户单击屏幕上的对象。基于所选对象,绘制图(并将类的成员变量设置为所选对象)。通过该对象上的 CollectionChanged 事件接收对该选定对象位置的更新。该处理程序使用 Dispatcher.Invoke 编组到 UI 线程,在该线程中对绘图进行其他更新。在任何时候,用户都可以“取消选择”对象。发生这种情况时,所选项目的 class\' 成员变量设置为 null。问题是,可以随时接收更新。发生这种情况时,它会尝试访问现在为 null 的成员变量上的数据。我已经尝试过 Lock、mutex、semaphore、SemiphoreSlim,其 ctor 值为 1,1 等。但是似乎没有什么能阻止 UI 线程尝试访问集合更改事件的事件处理程序中的成员变量和用户选择事件处理程序(通过依赖对象值更改事件)同时访问成员变量。

DependencyObject 值更改事件处理程序(删除了无关代码):

            try
            {
                mSemaphore.Wait();
                if (mTrackHistory != null)
                {
                    mTrackHistory.PositionData.CollectionChanged -= 
                    PositionData_CollectionChanged;
                }

                mTrackHistory = e.NewValue as TrackHistoryInfo;

                // If track history is null, don\'t do anything....
                if (mTrackHistory != null)
                {
                    Create initial plot
                }
            }
            finally
            {
                mSemaphore.Release();
            }
        }

和 CollectionChanged 事件处理程序:

if (mDispatcher != Dispatcher.CurrentDispatcher)
            {
                mDispatcher.Invoke(() => PositionData_CollectionChanged(sender, e));
            }
            else
            {
                try
                {
                    mSemaphore.Wait();
                    if (e.Action == 
                    System.Collections.Specialized.NotifyCollectionChangedAction.Add)
                    {
                        // Update the plot...
                        for (int idx = 0; idx < e.NewItems.Count; idx++)
                        {
                            (ECEF Position, DateTime Time) newInfo = ((ECEF Position, DateTime 
                            Time))e.NewItems[idx];
                            
                            // This is where it blows up attempting to access a null mTrackHistory
                            TimeSpan ts = newInfo.Time - mTrackHistory.StandupTime;
                            string stamp = $\"{ts.Hours:D2}:{ts.Minutes:D2}:{ts.Seconds:D2}\";

                           // Update plot with latest data
                       }
                            
                }
                finally
                {
                    mSemaphore.Release();
                }

非常感谢任何有关如何防止此问题的建议/指导!

    标签: c# multithreading reentrancy


    【解决方案1】:

    在进行任何类型的背景计算时,我会推荐类似以下模式的内容:

    public void OnObjectSelected(){
        var localSelectedObject = SelectedObjectProperty;
        if(localSelectedObject == null){
            ResetPlot();
            return
        }
    
        var result = await Task.Run(() => DoWorkOnBackgroundThread(localSelectedObject));
    
        // The selected object could have changed while the background thread was working
        // So check if it is still the right data to update
        if(localSelectedObject == SelectedObjectProperty){
            UpdatePlot(localSelectedObject);
        }
    }
    

    这种模式消除了对 Dispatch.Invoke 的任何需求,因为除了 DoWorkOnBackgroundThread 之外的所有内容都在 UI 线程上运行。您也不必担心将选定对象设置为 null,因为后台线程使用保证永远不会更改的本地副本。

    如果所选对象发生更改,您可能还想为cancelling 后台工作添加功能,因为不再需要结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-03
      • 2012-03-16
      • 1970-01-01
      • 2016-04-04
      • 2016-11-10
      • 2015-02-17
      • 1970-01-01
      • 2020-01-26
      相关资源
      最近更新 更多