需要针对我的用例进行高效设计的建议
Need suggestions for efficient design for my use case
我正在尝试将第三方库集成到我的 WPF 应用程序中。该库用于通过 his/her 社会安全号码收集某人的信用卡历史记录。它基本上提供了一种 returns 无效的方法。完成该方法后,您可以收集信用卡历史记录。
ThirdPartyLibrary Instance = new Instance(SSN);
Instance.Run(); // This method returns a void and it blocks until data retrieval completes
if ( Instance.Success )
CreditHistory History = Instance.CreditHistory;
现在我想同时为多人运行此 API,但是,我还希望有一次最大请求数的阈值。如果达到该阈值,我想在发送新请求之前等待其中一个请求完成。最后,我想更新我的 UI,因为任何一个提交的请求成功或失败。
这是我的问题:
这个第三方API可能要等很长时间才能收集到信息。使用 TPL/PLINQ API 之类的 Parallel.ForEach
或 AsParallel
是否适合这种情况?
如果第三方库的 Run 方法 returns 无效,我可以使用 await/async
创建对第三方库调用的包装器吗?
等待个人请求提交的最佳方法是什么,这样我就可以不断更新我的 UI 进度。
请提供您的建议(以及一些示例代码)应该如何 designed/implemented?我一直在阅读有关 Tasks 和 Async/Await 的大量资料,但对这个用例的最佳模式是什么感到很困惑?
我不能说哪种做法是最好的。
您可以使用 MSDN 中有关 Degree of Parallelism. But this will require to install the System.Threading.Tasks.Dataflow 命名空间的示例。
Parallel class 有选项,你也可以在其中指定并行度:
var myOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.For(0, NumberOfClients, myOptions, i=>
{
// Code to run
});
因此,第一个问题的答案是是,您的列表非常适合完成您的任务。
回答第二个:是,可以。
最后一个:
在 UI 线程上,您可以这样调用:
Task.Factory.StartNew(DoWork);
您可以使用CancellationToken in order to support cancellation. You also can use other overloads of the method, in order to obtain required behavior. You also can have the Value property, which will denote how much records was proceeded. This property you can bound with ProgressBar,并适当更新它
public void DoWork()
{
var myOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.For(0, NumberOfClients, myOptions, i=>
{
ThirdPartyLibrary Instance = new Instance(SSN[i]);
Instance.Run();
Interlocked.Increment(ref value);
RaisePropertyChanged("Value");
});
}
关于Interlocked.Increment(ref value): it performs increment of the specified variable and store the result, as an atomic operation, in order to prevent the data race condition的几句话。
正如@l3arnon 指出的那样,您的 API 是同步的(即使它正在执行 I/O-based 操作,这自然是异步的)。因此,您需要将其视为阻塞。
在不了解您的要求的情况下,我建议 async
和 Task.Run
。您也可以使用 Parallel
或 Parallel LINQ (AsParallel
),但当您一开始就知道操作总数时,它们的效果最好,目前尚不清楚在这种情况下是否属于这种情况。此外,如果您必须在其他操作正在进行时添加操作,您还可以考虑 TPL Dataflow
和 Rx
。 IMO,async
方法的学习曲线最低。然而,Rx 的设计尤其优雅,一旦你学会了它,你就会开始在任何地方使用它。
首先,您可能需要一种使用自然 return 类型和异常进行错误处理的实用程序方法。我不确定为什么您的第三方 class 还没有这样的东西:
CreditHistory GetCreditHistory(string ssn)
{
var instance = new Instance(ssn);
instance.Run();
if (instance.Success)
return instance.CreditHistory;
throw ...
}
在 async
情况下,您将开始编写应从 UI 线程调用的方法,并在后台执行工作:
async Task GetCreditHistoryOnBackgroundThreadAsync(string ssn)
{
try
{
var history = await Task.Run(() => GetCreditHistory(ssn));
// Update UI with credit history.
}
catch (Exception ex)
{
// Update UI with error details.
}
}
然后你可以添加异步节流。这是一种方法,使用内置 SemaphoreSlim
:
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(10);
async Task GetCreditHistoryOnBackgroundThreadAsync(string ssn)
{
await _mutex.WaitAsync();
try
{
var history = await Task.Run(() => GetCreditHistory(ssn));
// Update UI with credit history.
}
catch (Exception ex)
{
// Update UI with error details.
}
finally
{
_mutex.Release();
}
}
我正在尝试将第三方库集成到我的 WPF 应用程序中。该库用于通过 his/her 社会安全号码收集某人的信用卡历史记录。它基本上提供了一种 returns 无效的方法。完成该方法后,您可以收集信用卡历史记录。
ThirdPartyLibrary Instance = new Instance(SSN);
Instance.Run(); // This method returns a void and it blocks until data retrieval completes
if ( Instance.Success )
CreditHistory History = Instance.CreditHistory;
现在我想同时为多人运行此 API,但是,我还希望有一次最大请求数的阈值。如果达到该阈值,我想在发送新请求之前等待其中一个请求完成。最后,我想更新我的 UI,因为任何一个提交的请求成功或失败。
这是我的问题:
这个第三方API可能要等很长时间才能收集到信息。使用 TPL/PLINQ API 之类的
Parallel.ForEach
或AsParallel
是否适合这种情况?如果第三方库的 Run 方法 returns 无效,我可以使用
await/async
创建对第三方库调用的包装器吗?等待个人请求提交的最佳方法是什么,这样我就可以不断更新我的 UI 进度。
请提供您的建议(以及一些示例代码)应该如何 designed/implemented?我一直在阅读有关 Tasks 和 Async/Await 的大量资料,但对这个用例的最佳模式是什么感到很困惑?
我不能说哪种做法是最好的。
您可以使用 MSDN 中有关 Degree of Parallelism. But this will require to install the System.Threading.Tasks.Dataflow 命名空间的示例。
Parallel class 有选项,你也可以在其中指定并行度:
var myOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.For(0, NumberOfClients, myOptions, i=>
{
// Code to run
});
因此,第一个问题的答案是是,您的列表非常适合完成您的任务。
回答第二个:是,可以。
最后一个:
在 UI 线程上,您可以这样调用:
Task.Factory.StartNew(DoWork);
您可以使用CancellationToken in order to support cancellation. You also can use other overloads of the method, in order to obtain required behavior. You also can have the Value property, which will denote how much records was proceeded. This property you can bound with ProgressBar,并适当更新它
public void DoWork()
{
var myOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.For(0, NumberOfClients, myOptions, i=>
{
ThirdPartyLibrary Instance = new Instance(SSN[i]);
Instance.Run();
Interlocked.Increment(ref value);
RaisePropertyChanged("Value");
});
}
关于Interlocked.Increment(ref value): it performs increment of the specified variable and store the result, as an atomic operation, in order to prevent the data race condition的几句话。
正如@l3arnon 指出的那样,您的 API 是同步的(即使它正在执行 I/O-based 操作,这自然是异步的)。因此,您需要将其视为阻塞。
在不了解您的要求的情况下,我建议 async
和 Task.Run
。您也可以使用 Parallel
或 Parallel LINQ (AsParallel
),但当您一开始就知道操作总数时,它们的效果最好,目前尚不清楚在这种情况下是否属于这种情况。此外,如果您必须在其他操作正在进行时添加操作,您还可以考虑 TPL Dataflow
和 Rx
。 IMO,async
方法的学习曲线最低。然而,Rx 的设计尤其优雅,一旦你学会了它,你就会开始在任何地方使用它。
首先,您可能需要一种使用自然 return 类型和异常进行错误处理的实用程序方法。我不确定为什么您的第三方 class 还没有这样的东西:
CreditHistory GetCreditHistory(string ssn)
{
var instance = new Instance(ssn);
instance.Run();
if (instance.Success)
return instance.CreditHistory;
throw ...
}
在 async
情况下,您将开始编写应从 UI 线程调用的方法,并在后台执行工作:
async Task GetCreditHistoryOnBackgroundThreadAsync(string ssn)
{
try
{
var history = await Task.Run(() => GetCreditHistory(ssn));
// Update UI with credit history.
}
catch (Exception ex)
{
// Update UI with error details.
}
}
然后你可以添加异步节流。这是一种方法,使用内置 SemaphoreSlim
:
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(10);
async Task GetCreditHistoryOnBackgroundThreadAsync(string ssn)
{
await _mutex.WaitAsync();
try
{
var history = await Task.Run(() => GetCreditHistory(ssn));
// Update UI with credit history.
}
catch (Exception ex)
{
// Update UI with error details.
}
finally
{
_mutex.Release();
}
}