【问题标题】:Invoking function on another thread in .Net Core在 .Net Core 的另一个线程上调用函数
【发布时间】:2021-07-19 17:04:51
【问题描述】:

我有一个后台线程有一个长时间运行的任务。

后台线程根据给定的过滤器搜索文件。 该任务可能运行很长时间,所以我不想等待任务完成才能显示一些结果。

此外,我不想锁定我的 UI 线程以检查后台任务是否有新结果。

我宁愿通知我的主线程:“嘿,添加了一个新的搜索结果”。
有点像 Windows 资源管理器,在搜索仍在进行时显示搜索结果:

foreach (FileInfo itemToFilter in unfilteredSearchResults)
{
    if (extension == ".wav" || extension == ".flac" || extension == ".mp3")
    {
        // read audio information such as tags etc. Might take some time per file
        WaveFileLib.WaveFile.WAVEFile audioItem = new WaveFileLib.WaveFile.WAVEFile(audioFile.FullName);
        // Compare audio information such as tags, title, license, date, length etc to filter        
        if (Filter.AudioItemMatchesFilter(ref audioItem))
        {
            lock (threadLock)
            {
                // add search result to list of filtered audio files
                this.AudioItemList.Add(audioItem);
            }
            // notify main thread in order to refresh ui (if applicable)
            ParentView.AudioItemAdded();
        }
    }
}

然后主线程可以从新添加的项目生成视图。
以前,使用 BeginInvoke 很容易做到这一点,但使用 .net core 时,这种可能性似乎消失了。

通知主线程更新搜索结果的替代方案/选项是什么?

【问题讨论】:

  • 使用Progress 类。它就是为了这个原因而创建的

标签: c# multithreading user-interface .net-core


【解决方案1】:

正如 Panagiotis Kanavos 所说的

public class AudioFileSearcher
{
    // task caller
    public AudioFileSearcher(string searchPath, bool includeSubFolders, Views.SoundListView.SoundList parentView)
    {
        this.progress1 = new Progress<int>(
        {
            backgroundWorker1_ProgressChanged();
        });
        Task.Run(async () => await FindAudioFiles(progress1));
    }
    // reference to be updated by task and read by event Handler
    private Progress<int> progress1 { get; set; }
    // do something on task progress
    void backgroundWorker1_ProgressChanged()
    {
        // Update UI on main thread or whatever
    }
    // Task
    public async Task FindAudioFiles(IProgress<int> progress)
    {
        foreach(AudioItem itemToProcess in allFoundaudioItems)
        {
            // do long processing in background task
            // a new element has been generated, update UI
            if (progress != null) progress.Report(0); // ~fire event
        }
    }
}

像魅力一样工作,正在加载物品

【讨论】:

    【解决方案2】:

    您应该使用 Microsoft 的响应式框架(又名 Rx)- NuGet System.Reactive 并添加 using System.Reactive.Linq; - 然后您可以这样做:

    IObservable<WaveFileLib.WaveFile.WAVEFile> query =
        from itemToFilter in unfilteredSearchResults.ToObservable()
        where extension == ".wav" || extension == ".flac" || extension == ".mp3"
        from audioItem in Observable.Start(() => new WaveFileLib.WaveFile.WAVEFile(audioFile.FullName))
        from x in Observable.Start(() =>
        {
            var a = audioItem;
            var flag = Filter.AudioItemMatchesFilter(ref a);
            return new { flag, audioItem = a };
        })
        where x.flag
        select x.audioItem;
        
    IDisposable subscription =
        query
            .ObserveOnDispatcher()
            .Subscribe(audioItem =>
            {
                /* Your code to update the UI here */
            });
    

    我不清楚是创建一个新的WAVEFile 还是调用AudioItemMatchesFilter 需要时间,所以我将两者都包裹在Observable.Start 中。你还打了一个ref 电话,这让代码有点乱。

    尽管如此,此代码在后台处理所有调用,并在结果进入调度程序线程时自动将它们移动。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多