You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用IAsyncEnumerable时先调用StartQueryingFromFiles致参数为空求助

问题描述

基于soundfingerprinting库尝试用IAsyncEnumerable<string>读取文件夹所有文件路径,但调用StartQueryingFromFiles(IAsyncEnumerable<string> files, CancellationToken token)时,该方法会在IAsyncEnumerable的迭代逻辑执行前就运行,导致传入的文件流始终为空。

按钮点击事件代码

private void btnStarts_Click(object sender, EventArgs e, Label searchLabel, TabPage page, ProgressBar bar)
{
    try
    {
        bar.Style = ProgressBarStyle.Marquee;
        var searchPath = searchLabel.Text; //Path to folder
        var tokenSource = new CancellationTokenSource();
        var bolFinished = false;

        if (!string.IsNullOrEmpty(searchPath) && Directory.Exists(searchPath))
        {
            Task.Run(() =>
            {   
                //The method below gets called first before the iteration                     
                StartQueryingFromFiles(FetchAllItems(searchPath), tokenSource.Token);
                bolFinished = true;
            });
        }
        else
        {
            MessageBox.Show("No Audio Files Found.");
        }

        if(bolFinished)
            bar.Style = ProgressBarStyle.Blocks;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

文件枚举方法

//This gets called first, its supposed to return all files, but returns nothing
IAsyncEnumerable<string> FetchAllItems(string searchPath)
{
    return FetchItems(searchPath);
}

//This is called last, I don't know why. It reads the file paths in the folder
async IAsyncEnumerable<string> FetchItems(string searchPath)
{
    foreach (var item in Directory.EnumerateFiles(searchPath))
    {
        await Task.Delay(100);
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now.ToLongTimeString()} Reading: {item}");
        yield return item;
    }
}

StartQueryingFromFiles方法

public void StartQueryingFromFiles(IAsyncEnumerable<string> files, CancellationToken token)
{
    _ = QueryCommandBuilder.Instance
        .BuildRealtimeQueryCommand()
        .From(files, MediaType.Audio)
        .WithRealtimeQueryConfig(config =>
        {
    // provide a success callback that will be invoked for matches that pass result entry filter
    config.SuccessCallback = result =>
            {
                foreach (var entry in result.ResultEntries)
                {
                    System.Diagnostics.Debug.WriteLine($"Successfully matched {entry.TrackId}");
                }
            };

    // configure result entry filter
    config.ResultEntryFilter = new TrackMatchLengthEntryFilter(5d);
            return config;
        })
        .UsingServices(modelService, mediaService)
        .Query(token);
}

问题原因与修复方案

核心问题

  1. IAsyncEnumerable延迟执行特性:FetchItems中的迭代逻辑(yield return)只有当流被消费时才会触发,这是设计特性,但代码存在异步等待缺失,导致状态判断错误。
  2. 未等待异步操作完成:Task.Run内调用的StartQueryingFromFiles启动了异步Query操作,但未等待其完成就标记bolFinished = true,UI线程的状态判断会在异步任务刚启动时就执行,完全无效。
  3. 跨线程UI更新风险:直接在非UI线程更新进度条控件,可能引发异常。

具体修复步骤

  1. 将StartQueryingFromFiles改为异步方法,等待查询操作完成:
public async Task StartQueryingFromFiles(IAsyncEnumerable<string> files, CancellationToken token)
{
    await QueryCommandBuilder.Instance
        .BuildRealtimeQueryCommand()
        .From(files, MediaType.Audio)
        .WithRealtimeQueryConfig(config =>
        {
            config.SuccessCallback = result =>
            {
                foreach (var entry in result.ResultEntries)
                {
                    System.Diagnostics.Debug.WriteLine($"Successfully matched {entry.TrackId}");
                }
            };
            config.ResultEntryFilter = new TrackMatchLengthEntryFilter(5d);
            return config;
        })
        .UsingServices(modelService, mediaService)
        .Query(token);
}
  1. 修改按钮点击事件,正确等待异步任务并安全更新UI:
private async void btnStarts_Click(object sender, EventArgs e, Label searchLabel, TabPage page, ProgressBar bar)
{
    try
    {
        bar.Style = ProgressBarStyle.Marquee;
        var searchPath = searchLabel.Text;
        using var tokenSource = new CancellationTokenSource();

        if (!string.IsNullOrEmpty(searchPath) && Directory.Exists(searchPath))
        {
            await Task.Run(async () =>
            {   
                // 等待查询完成,确保IAsyncEnumerable流被正常消费
                await StartQueryingFromFiles(FetchAllItems(searchPath), tokenSource.Token);
            }, tokenSource.Token);
            
            // 回到UI线程更新进度条
            bar.Invoke(() => bar.Style = ProgressBarStyle.Blocks);
        }
        else
        {
            MessageBox.Show("No Audio Files Found.");
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
  1. 简化冗余的文件枚举方法(可选):
async IAsyncEnumerable<string> FetchAllItems(string searchPath)
{
    foreach (var item in Directory.EnumerateFiles(searchPath))
    {
        await Task.Delay(100);
        System.Diagnostics.Debug.WriteLine($"{DateTime.Now.ToLongTimeString()} Reading: {item}");
        yield return item;
    }
}

关键说明

  • IAsyncEnumerable的迭代逻辑只有在被消费时才会执行,确保异步操作被await后,库的From方法会正常遍历文件流,不会出现空参数问题。
  • 所有UI控件更新必须通过Invoke回到UI线程,避免跨线程异常。
  • 使用using管理CancellationTokenSource,确保资源正确释放。

内容的提问来源于stack exchange,提问作者Timothy Ayodele

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 12:55:25