我有这样的代码:
public void IssueOrders(List<OrderAction> actions)
{
foreach (var action in actions)
{
if (action is AddOrder)
{
uint userId = apiTransactions.PlaceOrder((action as AddOrder).order);
Console.WriteLine("order is placing userId = " + userId);
}
// TODO: implement other actions
}
// how to wait until OnApiTransactionsDataMessageReceived for all userId is received?
// TODO: need to update actions with received data here
}
private void OnApiTransactionsDataMessageReceived(object sender, DataMessageReceivedEventArgs e)
{
var dataMsg = e.message;
var userId = dataMsg.UserId;
apiTransactions.PlaceOrder
是异步的,因此我收到userId
作为结果,但我将在回调OnApiTransactionsDataMessageReceived中接收数据。
例如,如果我下3个订单,我会收到3个userId,例如1
,3
和4
。现在我需要等到收到所有这些userId的数据。
userId
总是在增加。这几乎是整数序列,但由于并行执行,可能会省略一些数字。
UPD 注意:
UPD2
可能我需要重构下面的PlaceOrder代码,以便我可以保证在收到“回调”之前知道userId
:
public uint PlaceOrder(Order order)
{
Publisher pub = GetPublisher();
SchemeDesc schemeDesc = pub.Scheme;
MessageDesc messageDesc = schemeDesc.Messages[0]; //AddMM
FieldDesc fieldDesc = messageDesc.Fields[3];
Message sendMessage = pub.NewMessage(MessageKeyType.KeyName, "FutAddOrder");
DataMessage smsg = (DataMessage)sendMessage;
uint userId = counter.Next();
FillDataMessageWithPlaceOrder(smsg, order, userId);
System.Console.WriteLine("posting message dump: {0}", sendMessage);
pub.Post(sendMessage, PublishFlag.NeedReply);
sendMessage.Dispose();
return userId;
}
所以我需要将PlaceOrder拆分为两个方法:userId CreateOrder
和void PostOrder
。这将保证在收到回叫时我知道userId
。
答案 0 :(得分:1)
我会在Reactive Framework中查看ForkJoin方法。它将阻塞,直到多个异步调用完成。
编辑:似乎ForkJoin()只包含在Rx的实验版中。这是基于Merge()的所需内容discussion。
答案 1 :(得分:0)
最愚蠢和最有效的方法之一是:
public void IssueOrders(List<OrderAction> actions)
{
var userIds = new List<uint>();
lock(theHashMap)
theHashMap[userIds] = "blargh";
foreach (var action in actions)
{
if (action is AddOrder)
{
lock(userIds)
{
uint userId = apiTransactions.PlaceOrder((action as AddOrder).order);
Console.WriteLine("order is placing userId = " + userId);
userIds.Add(userId);
}
}
// TODO: implement other actions
}
// waiting:
do
{
lock(userIds)
if(userIds.Count == 0)
break;
Thread.Sleep(???); // adjust the time depending on how long you wait for a callback on average
}while(true);
lock(theHashMap)
theHashMap.Remove(userIds);
// now you have the guarantee that all were received
}
private Dictionary<List<uint>, string> theHashMap = new Dictionary<List<uint>,string>();
private void OnApiTransactionsDataMessageReceived(object sender, DataMessageReceivedEventArgs e)
{
var dataMsg = e.message;
var userId = dataMsg.UserId;
// do some other things
lock(theHashMap)
foreach(var list in theHashMap.Keys)
lock(list)
if(list.Remove(userId))
break;
}
但是,这是非常粗暴的方法..除非你通过等待解释你的意思,否则很难提出更多建议 - 正如Jon在评论中所说的那样。例如,您可能想要离开IssueOrders
,等待任何地方,并确保在所有工作到达时完成一些额外的工作?或者你可能不能离开IssueOrders
除非收到所有人?等。
编辑:请注意,在ADD附近,锁定必须在之前 PlaceOrder,否则,当回调达到超高速时,回调可能会在添加之前尝试删除ID。另请注意,此实现非常简单:回调必须每次搜索并锁定所有列表。使用一些额外的字典/地图/索引,它可能会进行很多优化,但为了便于阅读,我没有这样做。
答案 2 :(得分:0)
如果您能够更改API,请考虑使用Task Parallel Library,您的代码将变得更加容易。
否则AutoResetEvent可能会对您有所帮助:
private Dictionary<int, AutoResetEvent> m_Events = new ...;
public void IssueOrders(List<OrderAction> actions)
{
foreach (var action in actions)
{
if (action is AddOrder)
{
uint userId = apiTransactions.PlaceOrder((action as AddOrder).order);
// Attention: Race condition if PlaceOrder finishes
// before the MRE is created and added to the dictionary!
m_Events[userId] = new ManualResetEvent(false);
Console.WriteLine("order is placing userId = " + userId);
}
// TODO: implement other actions
}
WaitHandle.WaitAll(m_Events.Values);
// TODO: Dispose the created MREs
}
private void OnApiTransactionsDataMessageReceived(object sender, DataMessageReceivedEventArgs e)
{
var dataMsg = e.message;
var userId = dataMsg.UserId;
m_Events[userId].Set();
}