如果我知道 API 在某个时候执行 I/O,我是否应该异步调用棕地 API?
Should I make a call to a brownfield API asynchronous if I know the API does I/O at some point?
我正在开发一个项目,该项目使用我们编写的旧 API(即不是第三方,但目前未在开发中)。旧的 API 执行非托管 I/O 操作:它通过运行时可调用包装器 (RCW) 使用 COM DLL 连接到中间层服务器。这个新项目本身将成为一个新应用程序的 API,正如我所说,它利用了旧的 API;但是,我想在适当的时候公开异步方法,因为我还不确定我将如何使用新的 API。例如,如果我在 Web 应用程序中使用它,我想确保我不会阻塞不必要地等待 COM DLL 深处发生的 I/O 操作的线程。
这是我的新应用程序的样子:
------------------------------
| CLIENT (WIN32, WEB, ETC.) |
-------------------------------
------------------------------
| NEW API |
-------------------------------
------------------------------
| OLD API |
-------------------------------
------------------------------
| COM DLL |
-------------------------------
这真的很简单,看起来新的 API 的唯一目的是包装旧的 API,但新的 API 是一个单独的应用程序,它具有它自己的业务逻辑,并使用旧的 API 进行一定比例的操作。
我已经阅读了很多关于 async/await 的适当使用的内容,尤其是在棕地应用程序中; Stephen Cleary's MSDN Magazine article on the topic for example is very helpful. I'm struggling in my case, though. If I'm not directly making use of a truly-asynchronous I/O method, e.g. HttpClient.GetAsync, but I know (or am reasonably sure) the COM DLL is doing I/O without a thread, is there still no thread?换句话说,线程是否在通过旧同步 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 ops 时,线程会被放弃吗?如果没有,是否有更好的方法使用 async/await 来确保最有效地使用异步?
I'm using Task.Run to call the synchronous methods in the old API. When they hit I/O ops, will the thread be relinquished? If not, is there a better way to use async/await to ensure the most-efficient use of asynchrony?
没有;恐怕旧的 API 是同步的,你不能 "force" 它是异步的。
If I'm not directly making use of a truly-asynchronous I/O method... but I know (or am reasonably sure) the COM DLL is doing I/O without a thread, is there still no thread? In other words, does the thread get relinquished as soon as it hits the I/O operation deep down inside the COM DLL called via the old synchronous API?
所有 I/O 本质上都是异步的,但在这种情况下,第一个同步调用会阻塞 I/O 上的线程。因此,即使 COM DLL 是异步的,旧的 API 也只会公开同步的 APIs - 旧的 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);
我正在开发一个项目,该项目使用我们编写的旧 API(即不是第三方,但目前未在开发中)。旧的 API 执行非托管 I/O 操作:它通过运行时可调用包装器 (RCW) 使用 COM DLL 连接到中间层服务器。这个新项目本身将成为一个新应用程序的 API,正如我所说,它利用了旧的 API;但是,我想在适当的时候公开异步方法,因为我还不确定我将如何使用新的 API。例如,如果我在 Web 应用程序中使用它,我想确保我不会阻塞不必要地等待 COM DLL 深处发生的 I/O 操作的线程。
这是我的新应用程序的样子:
------------------------------
| CLIENT (WIN32, WEB, ETC.) |
-------------------------------
------------------------------
| NEW API |
-------------------------------
------------------------------
| OLD API |
-------------------------------
------------------------------
| COM DLL |
-------------------------------
这真的很简单,看起来新的 API 的唯一目的是包装旧的 API,但新的 API 是一个单独的应用程序,它具有它自己的业务逻辑,并使用旧的 API 进行一定比例的操作。
我已经阅读了很多关于 async/await 的适当使用的内容,尤其是在棕地应用程序中; Stephen Cleary's MSDN Magazine article on the topic for example is very helpful. I'm struggling in my case, though. If I'm not directly making use of a truly-asynchronous I/O method, e.g. HttpClient.GetAsync, but I know (or am reasonably sure) the COM DLL is doing I/O without a thread, is there still no thread?换句话说,线程是否在通过旧同步 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 ops 时,线程会被放弃吗?如果没有,是否有更好的方法使用 async/await 来确保最有效地使用异步?
I'm using Task.Run to call the synchronous methods in the old API. When they hit I/O ops, will the thread be relinquished? If not, is there a better way to use async/await to ensure the most-efficient use of asynchrony?
没有;恐怕旧的 API 是同步的,你不能 "force" 它是异步的。
If I'm not directly making use of a truly-asynchronous I/O method... but I know (or am reasonably sure) the COM DLL is doing I/O without a thread, is there still no thread? In other words, does the thread get relinquished as soon as it hits the I/O operation deep down inside the COM DLL called via the old synchronous API?
所有 I/O 本质上都是异步的,但在这种情况下,第一个同步调用会阻塞 I/O 上的线程。因此,即使 COM DLL 是异步的,旧的 API 也只会公开同步的 APIs - 旧的 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);