[英]Concatenate two infinite C# IEnumerables together in no particular order
I have two methods that each return infinite IEnumerable
that never ends.我有两种方法,每个方法都返回永无止境的无限
IEnumerable
。 I want to concatenate them so whenever any of the IEnumerable
s return a value, I can instantly get and process it.我想将它们连接起来,这样每当任何
IEnumerable
返回一个值时,我都可以立即获取并处理它。
static void Main(string[] args)
{
var streamOfBoth = get1().Concat(get2());
foreach(var item in streamOfBoth)
{
Console.WriteLine(item);
// I'd expect mixed numbers 1 and 2
// Instead I receive only 1s
}
}
static IEnumerable<int> get1()
{
while (true)
{
System.Threading.Thread.Sleep(1000);
yield return 1;
}
}
static IEnumerable<int> get2()
{
while (true)
{
System.Threading.Thread.Sleep(200);
yield return 2;
}
}
Is there a way to do this with IEnumerables without having to use threads?有没有办法用 IEnumerables 做到这一点而不必使用线程?
This is fairly easily achieved with System.Reactive
这很容易通过
System.Reactive
实现
static void Main()
{
get1().ToObservable(TaskPoolScheduler.Default).Subscribe(Print);
get2().ToObservable(TaskPoolScheduler.Default).Subscribe(Print);
}
static void Print(int i)
{
Console.WriteLine(i);
}
static IEnumerable<int> get1()
{
while (true)
{
System.Threading.Thread.Sleep(1000);
yield return 1;
}
}
static IEnumerable<int> get2()
{
while (true)
{
System.Threading.Thread.Sleep(200);
yield return 2;
}
}
This produces the following output on my machine:这会在我的机器上产生以下输出:
2 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 ...
Note that ToObservable
is called with the argument TaskPoolScheduler.Default
;需要注意的是
ToObservable
时调用参数TaskPoolScheduler.Default
; just calling ToObservable
without it will result in synchronous execution, meaning it will keep enumerating the first sequence forever and never get to the second one.仅
ToObservable
没有它的情况下调用ToObservable
将导致同步执行,这意味着它将永远枚举第一个序列而永远不会到达第二个序列。
You may want to interleave get1
and get2
(take item from get1
, then from get2
, then again from get1
and then from get2
etc.).您可能希望交织
get1
和get2
(从拿项目get1
,然后从get2
从,然后再get1
,然后从get2
等)。 Generalized ( IEnumerable<T>
are not necessary infinite and of the same size) Interleave<T>
extension method can be like this:广义(
IEnumerable<T>
不一定是无限的且大小相同) Interleave<T>
扩展方法可以是这样的:
public static partial class EnumerableExtensions {
public static IEnumerable<T> Interleave<T>(this IEnumerable<T> source,
params IEnumerable<T>[] others) {
if (null == source)
throw new ArgumentNullException(nameof(source));
else if (null == others)
throw new ArgumentNullException(nameof(others));
IEnumerator<T>[] enums = new IEnumerator<T>[] { source.GetEnumerator() }
.Concat(others
.Where(item => item != null)
.Select(item => item.GetEnumerator()))
.ToArray();
try {
bool hasValue = true;
while (hasValue) {
hasValue = false;
for (int i = 0; i < enums.Length; ++i) {
if (enums[i] != null && enums[i].MoveNext()) {
hasValue = true;
yield return enums[i].Current;
}
else {
enums[i].Dispose();
enums[i] = null;
}
}
}
}
finally {
for (int i = enums.Length - 1; i >= 0; --i)
if (enums[i] != null)
enums[i].Dispose();
}
}
}
Then use it:然后使用它:
var streamOfBoth = get1().Interleave(get2());
foreach(var item in streamOfBoth)
{
Console.WriteLine(item);
}
Edit: if编辑:如果
" whenever any ... return a value, I can instantly get and process"
“只要有任何......返回一个值,我就可以立即获取并处理”
is crucial phrase in your question you can try BlockingCollection
and implementproducer-consumer pattern :是您问题中的关键短语,您可以尝试
BlockingCollection
并实现生产者-消费者模式:
static BlockingCollection<int> streamOfBoth = new BlockingCollection<int>();
// Producer #1
static void get1() {
while (true) {
System.Threading.Thread.Sleep(1000);
streamOfBoth.Add(1); // value (1) is ready and pushed into streamOfBoth
}
}
// Producer #2
static void get2() {
while (true) {
System.Threading.Thread.Sleep(200);
streamOfBoth.Add(2); // value (2) is ready and pushed into streamOfBoth
}
}
...
Task.Run(() => get1()); // Start producer #1
Task.Run(() => get2()); // Start producer #2
...
// Cosumer: when either Producer #1 or Producer #2 create a value
// consumer can starts process it
foreach(var item in streamOfBoth.GetConsumingEnumerable()) {
Console.WriteLine(item);
}
Here is a generic Merge
method that merges IEnumerable
s.这是合并
IEnumerable
的通用Merge
方法。 Each IEnumerable
is enumerated in a dedicated thread.每个
IEnumerable
都在一个专用线程中枚举。
using System.Reactive.Linq;
using System.Reactive.Concurrency;
public static IEnumerable<T> Merge<T>(params IEnumerable<T>[] sources)
{
IEnumerable<IObservable<T>> observables = sources
.Select(source => source.ToObservable(NewThreadScheduler.Default));
IObservable<T> merged = Observable.Merge(observables);
return merged.ToEnumerable();
}
Usage example:用法示例:
var streamOfBoth = Merge(get1(), get2());
Enumerating the resulting IEnumerable
will block the current thread until the enumeration is finished.枚举生成的
IEnumerable
将阻塞当前线程,直到枚举完成。
This implementation depends on the System.Reactive and System.Interactive.Async packages.此实现取决于System.Reactive和System.Interactive.Async包。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.