我正在开发一个使用我们编写的旧API的项目(即不是第三方,但目前还没有开发)。旧API执行非托管I/O操作:它通过运行时可调用包装器(RCWs)使用COM DLL连接到中间层服务器。这个新项目本身将是一个新应用程序的API,正如我所说的那样,它使用了旧的API;但是,我想在适当的地方公开异步方法,因为我还不确定我将如何使用新的API。例如,如果我在web应用程序中使用它,我希望确保我不会不必要地阻塞线程,等待COM DLL内部正在发生的I/O操作。
下面是我的新应用程序的样子:
------------------------------
| CLIENT (WIN32, WEB, ETC.) |
-------------------------------
------------------------------
| NEW API |
-------------------------------
------------------------------
| OLD API |
-------------------------------
------------------------------
| COM DLL |
-------------------------------这确实简化了,使它看起来像是新API的唯一目的是包装旧API,但是新API是一个单独的应用程序,它有自己的业务逻辑,并且使用旧API来完成一定的操作。
我已经阅读了很多关于异步/等待的适当使用的文章,尤其是在brownfield应用程序中;例如,Stephen的MSDN杂志关于这一主题的文章非常有用。不过,我在为我的案子而挣扎。如果我没有直接使用真正的异步I/O方法,例如HttpClient.GetAsync,但我知道(或相当肯定) COM在没有线程的情况下执行I/O,有还是没有线吗?换句话说,线程一旦到达通过旧的同步API调用的COM DLL内部的I/O操作,就会被放弃吗?
这是一些示例代码。这是旧的API:
public class OldApi
{
public bool TryCheckOutDocument(int docNum, out Document document)
{
// use COM dll to "check out" the doc.
}
public bool TryCheckInDocument(Document document)
{
// " " "check in" the doc.
}
}
public class Document
{
public int DocNum { get; }
public object OtherData { get; set; }
}下面是新的API:
public class NewApi
{
public async Task ConvertDocsAsync(IEnumerable<int> docNums)
{
var oldApi = new OldApi();
Parallel.ForEach(docNums, async (docNum) =>
{
Document doc = null;
if (await Task.Run(() => !oldApi.TryCheckOutDocument(docNum, out doc)))
throw new Exception($"blah blah: {docNum}");
doc.OtherData = "this represents the conversion";
if (await Task.Run(() => !oldApi.TryCheckInDocument(doc)))
throw new Exception($"blah blah: {docNum}");
});
}
}我使用Task.Run调用旧API中的同步方法。当他们击中I/O操作时,线程会被放弃吗?如果没有,是否有更好的方法使用异步/等待以确保异步的最有效使用?
发布于 2017-07-11 18:19:01
我使用Task.Run调用旧API中的同步方法。当他们击中I/O操作时,线程会被放弃吗?如果没有,是否有更好的方法使用异步/等待以确保异步的最有效使用?
不,因为旧的API是同步的,所以您不能“强制”它是异步的。
如果我没有直接使用真正的异步I/O方法.但我知道(或相当肯定) COM DLL在没有线程的情况下执行I/O操作,还没有线程吗?换句话说,线程一旦到达通过旧的同步API调用的COM DLL内部的I/O操作,就会被放弃吗?
所有I/O本质上都是异步的,但在这种情况下,第一个同步调用是阻塞该I/O上的线程,所以即使COM DLL是异步的,旧API也只是公开同步API --旧API将阻塞线程。
此外,将async与Parallel结合使用肯定会带来痛苦的体验。
您现在最好的选择是保持同步:
Parallel.ForEach(docNums, docNum =>
{
Document doc = null;
if (!oldApi.TryCheckOutDocument(docNum, out doc))
throw new Exception($"blah blah: {docNum}");
doc.OtherData = "this represents the conversion";
if (!oldApi.TryCheckInDocument(doc))
throw new Exception($"blah blah: {docNum}");
});在用异步方法更新旧API之前,此时您可以执行异步并发:
var tasks = docNums.Select(async docNum =>
{
Document doc = await oldApi.CheckOutDocumentAsync(docNum);
doc.OtherData = "this represents the conversion";
await oldApi.CheckInDocumentAsync(doc);
});
await Task.WhenAll(tasks);https://stackoverflow.com/questions/45040896
复制相似问题