如何使用async并等待将活动(Pull-)Parser转换为被动Push-Parser?

时间:2017-07-06 18:53:44

标签: c# parsing async-await

我有一个Parser方法,它返回第一个与_Pattern匹配的位置。不幸的是,它正在积极地拉动角色。我希望它是被动的,由给出的字符驱动,这样我就可以让多个Parsers'同时'在同一个Stream上搜索不同的模式并返回第一个适合的模式。看起来应该是这样的:

Task<int>[] patterns = new Task<int>[] { };
var patternNo = Task.WaitAny(patterns);
var pattern1 = patterns[patternNo];
var found = pattern1.Result;

这是“拉”模式中的方法,用Func读取字符:

public int IndexIn(Func<char> reader) {
var currItem = reader();
for (int i, p = i = 0; ;) {
    if (p == -1 || _Pattern[p] == currItem) {
        ++i; ++p;
        currItem = reader();
    } else {
        p = _Prefix[p]; 
    }
    if (p >= _Pattern.Length) {
        return i - _Pattern.Length;
    }
}

我可以使用async / await轻松将其转换为推送驱动方法,这就是本框架之美:

public async Task<int> IsFound(Task<char> nextChar) {
    var currItem = await nextChar;
    for (int i, p = i = 0; ;) {
        if (p == -1 || _Pattern[p] == currItem) {
            ++i; ++p;
            currItem = await nextChar;
        } else {
            p = _Prefix[p]; 
        }
        if (p >= _Pattern.Length) {
            return i - _Pattern.Length;
        }
    }
}

但是如何'驱动'这个异步方法呢?我需要构建一个将角色传递给它的Task。给定一个字符串我想将它提供给模式,如:

var completion = new TaskCompletionSource<char>();
var isFound = searcher.IsFound(completion.Task);
foreach(var chr in stringToSearchIn) {
    completion.SetResult(chr);
    if (isFound.IsCompleted) {
        break;
    }
}
return isFound.Result;

但是,即使在检索到Result之后,TaskCompletionSource仍保持完整。我可以重置吗?有没有我可以使用的替代类,或者我必须编写自己的自定义TaskCompletionSource?或者任务是仅针对单次/值吗?

1 个答案:

答案 0 :(得分:0)

感谢您的努力。我在这里想要实现的是控制流的反转,这就是产量和异步/等待的结果。他们构建了一个可以在'yield'或'await'命令之后直接返回的状态机。 'yield'返回值,'await'检索值,因此使用这些Constructs可以在Read-Scenarios和Write-Scenarios中切换控制流。 在这种情况下,我可以提供一个Task-Factory,为我想要使用的每个角色创建一个新任务:

public class TaskFactory<T> {
    public TaskCompletionSource<T> Task { get; private set; }

    public Task<T> GetTask() {
        Task = new TaskCompletionSource<T>();
        return Task.Task;
    }
}

以下客户端代码按预期工作:

var completion = new TaskFactory<char>();
var isFound = searcher.IsFound(completion.GetTask);
foreach(var chr in hayStack) {
    completion.Task.SetResult(chr);
    if (isFound.IsCompleted) {
        break;
    }
}
return isFound.Result;

这会创建一些Overhead,这使得它比同步Push-Operation慢:

  1. Parser:创建TaskCompletionSource
  2. Parser:存储简历信息等待
  3. 驱动程序:填充当前的TaskCompletionSource,从而发出信号 完成
  4. Parser:恢复控制流程