C# 在DataReader中使用async/await?(没有中间缓冲区!)

C# 在DataReader中使用async/await?(没有中间缓冲区!),c#,asynchronous,io,async-await,.net-4.5,C#,Asynchronous,Io,Async Await,.net 4.5,我的目标很简单,我想进行异步I/O调用(使用async await)-但是: 不使用数据流依赖项() 没有中间缓冲区() 投影仪函数应作为参数发送。() 嗯 目前,这是我的代码,它的工作是从db读取并将每一行投影到一个Func 所以这里,MyClassFactory将是Func 那么我现在是如何运行它的呢 var sql = @"SELECT TOP 1000 [NAME],[datee] ,[val] FROM [WebERP].[dbo].[t]"; var a = GetSome

我的目标很简单,我想进行异步I/O调用(使用async await)-但是:

  • 不使用数据流依赖项()
  • 没有中间缓冲区()
  • 投影仪函数应作为参数发送。()

目前,这是我的代码,它的工作是从db读取并将每一行投影到一个
Func

所以这里,
MyClassFactory
将是
Func

那么我现在是如何运行它的呢

 var sql = @"SELECT TOP 1000 [NAME],[datee] ,[val]  FROM [WebERP].[dbo].[t]";
 var a = GetSomeData < MyClass > (sql, MyClass.MyClassFactory).Where(...); //notice the Func
var sql=@“从[WebERP].[dbo].[t]中选择前1000名的[NAME]、[datee]、[val];
var a=GetSomeData(sql,MyClass.MyClassFactory)//注意Func
一切正常。

问题从现在开始:

async
添加到方法中会产生一个错误:(是的,我知道Ienumerable是一个同步接口,因此存在问题)

public async Task>GetSomeData(字符串sql,Funcprojector)

不能是迭代器块,因为 'System.Threading.Tasks.Task>' 不是迭代器接口类型

-:

编译哪个

问题

如何将代码转换为支持完全异步IO调用

(条件下:无数据流依赖,发送投影仪函数作为参数,无中间缓冲区)

我想进行异步I/O调用(使用async await)-但是:

  • 不使用数据流依赖项(如本答案中所示)
  • 没有中间缓冲区(与此答案不同)
  • 投影仪函数应作为参数发送。(不像这个答案)
您可能想查看Stephen Toub的文章,了解一些关于如何处理异步数据序列的好主意

现在还不可能将
yield
wait
结合起来,但我想在这里做一个口头陈述:引用的需求没有列出
IEnumerable
和LINQ。因此,这里有一个可能的解决方案,其形状为两个协同程序(几乎未经测试)

数据生成器例程(对应于
IEnumarable
yield
):

协同程序执行助手(也可以作为一对实现):

公共类ProducerConsumerHub
{
TaskCompletionSource_consumer=新TaskCompletionSource();
TaskCompletionSource_producer=新TaskCompletionSource();
//TODO:使线程安全
公共异步任务ProduceAsync(T数据)
{
_producer.SetResult(数据);
等待消费者的任务;
_消费者=新任务完成源();
}
公共异步任务consumerasync()
{
var数据=等待_producer.Task;
_producer=新任务完成源();
_consumer.SetResult(空的.Value);
返回数据;
}
struct Empty{public static readonly Empty Value=default(Empty);}
}
这只是一个想法。对于这样一个简单的任务来说,这可能是一种过度的杀伤力,而且在某些方面(如线程安全、竞态条件和在不接触
producerTask
的情况下处理序列结尾)也可以得到改进。然而,它说明了异步数据检索和处理可能如何解耦。

这描述了另一种解决方案,即使用
Dasync/AsyncEnumerable

该库是开源的,在和上可用,并提供了一种可读的语法,可供现在使用,用于
IAsyncEnumerable
,直到以
异步的形式出现为止。。。收益返回
等待foreach


(我与该库没有任何联系;我发现它可能是一个非常有用的解决方案——我认为是什么!-在我正在开发的一个项目中,与您的问题相同。)

对于这种情况,如果C支持
iSyncumerator
,那就太好了。如果您能够以完全异步的方式返回一个急切填写的列表,那么问题就变得容易多了。
ToArray
确实创建了一个中间缓冲区。您的代码会迭代。编译的代码没有。IEnumerable不是问题所在。迭代(即:
yield-return
)就是问题所在。缓冲区有什么问题吗?@RoyiNamir怎么样?您必须向调用者公开每个元素的异步性。IEnumerable无法做到这一点。获取元素总是同步的。您需要使用异步模型,如IAsyncEnumerator。似乎有合理的图书馆围绕着这个想法()。没有内置的。此外,与所有ADO.NET和SQL工作相比,缓冲带来的性能影响非常小,因此避免缓冲不会对吞吐量产生任何有意义的影响。对于流式传输巨大的数据集.Tnx仍然有意义。我不知道如何将
GetSomeDataAsync
consumersomedataasync
一起使用。两者都使用sql命令???@RoyiNamir,
ConsumeSomeDataAsync
调用
GetSomeDataAsync
来启动序列,并简单地将
sql
字符串传递给它。我是否可以执行类似于
ConsumeSomeDataAsync()的操作。其中(…)
?(目前没有)@RoyiNamir,这里有一把简单的小提琴,展示了这个概念:。不,你不能使用标准LINQ。@RoyiNamir,我想你可以这么说。形象地说,这是生产者/消费者模式的特例,队列大小为1个元素,并且不阻塞任何线程。TPL数据流允许更复杂的场景。然后,还有Rx。
public class MyClass
{
    public static MyClass MyClassFactory(IDataRecord record)
    {
        return new MyClass
        {
            Name = record["Name"].ToString(),
            Datee = DateTime.Parse(record["Datee"].ToString()),
            val = decimal.Parse(record["val"].ToString())
        };
    }
    public string Name    {   get;   set;  }
    public DateTime Datee    {  get;     set;  }
    public decimal val    {  get;    set;    }
}
 var sql = @"SELECT TOP 1000 [NAME],[datee] ,[val]  FROM [WebERP].[dbo].[t]";
 var a = GetSomeData < MyClass > (sql, MyClass.MyClassFactory).Where(...); //notice the Func
public async Task GetSomeDataAsync<T>(
    string sql, Func<IDataRecord, T> projector, ProducerConsumerHub<T> hub)
{
    using (SqlConnection _conn = new SqlConnection(@"Data Source=..."))
    {
        using (SqlCommand _cmd = new SqlCommand(sql, _conn))
        {
            await _conn.OpenAsync();
            _cmd.CommandTimeout = 100000;
            using (var rdr = await _cmd.ExecuteReaderAsync())
            {
                while (await rdr.ReadAsync())
                    await hub.ProduceAsync(projector(rdr));
            }
        }
    }
}
public async Task ConsumeSomeDataAsync(string sql)
{
    var hub = new ProducerConsumerHub<IDataRecord>();
    var producerTask = GetSomeDataAsync(sql, rdr => rdr, hub);

    while (true)
    {
        var nextItemTask = hub.ConsumeAsync();
        await Task.WhenAny(producerTask, nextItemTask);

        if (nextItemTask.IsCompleted)
        {
            // process the next data item
            Console.WriteLine(await nextItemTask);
        }

        if (producerTask.IsCompleted)
        {
            // process the end of sequence
            await producerTask;
            break;
        }
    }
}
public class ProducerConsumerHub<T>
{
    TaskCompletionSource<Empty> _consumer = new TaskCompletionSource<Empty>();
    TaskCompletionSource<T> _producer = new TaskCompletionSource<T>();

    // TODO: make thread-safe
    public async Task ProduceAsync(T data)
    {
        _producer.SetResult(data);
        await _consumer.Task;
        _consumer = new TaskCompletionSource<Empty>();
    }

    public async Task<T> ConsumeAsync()
    {
        var data = await _producer.Task;
        _producer = new TaskCompletionSource<T>();
        _consumer.SetResult(Empty.Value);
        return data;
    }

    struct Empty { public static readonly Empty Value = default(Empty); }
}