我在嵌套循环中调用异步函数,如下面的
var queue = new Queue<ExchangeEmailInformation>(mailInformation);
var currentQueue = queue.ToList();
foreach (var exchangeEmailInformation in currentQueue)
{
ExchangeEmailInformation information = exchangeEmailInformation;
foreach (var queueList in exchangeEmailInformation.Attachment)
{
Attachment attachment = queueList;
information.FileName = attachment.Name;
var emailId = information.Sender.Split('@');
information.UserAlias = emailId[0];
information.FileSize = attachment.Size;
AddAttachmentAsync(information);
}
}
private static void AddAttachmentAsync(ExchangeEmailInformation information)
{
System.Threading.Tasks.Task.Factory.StartNew(
() =>
AddAttachment(information.UserAlias, information.EngagementName,
information.DocumentTransferId.ToString(), information.FileName,
information.FileSize.ToString(), information.ActivityName)).ContinueWith(
task => OnComplete(task, information), TaskContinuationOptions.None);
}
static void AddAttachment(string userAlias, string engagementName, string documentTranferId, string fileName, string fileSize,string activityName)
{
Console.Writeline(fileName);
}
In the exchange information collection has one record. In these collection there is another property called Attachment which type is AttachmentCollection which contain two attachments. After calling the method AddAttachmentAsync like above asynchronously the
打印结果
仅显示第二个附件(结果不正确)。
然后我尝试像下面一样执行同步。
private static void AddAttachmentAsync(ExchangeEmailInformation information)
{
AddAttachment(information.UserAlias, information.EngagementName,
information.DocumentTransferId.ToString(), information.FileName,
information.FileSize.ToString(), information.ActivityName);
}
结果是
FirstAttachment.txt
SecondAttachment.txt
按照我想要的方式显示正确的结果
我该如何解决这些问题?
答案 0 :(得分:5)
information
是在嵌套循环之外声明的引用类型对象。您将此对象传递给AddAttachmentAsync
方法,但在等待它完成(或甚至开始处理Task
)之前,您已在下一次迭代中修改information
。
在将information
发送到异步方法之前,您应该复制{{1}}。
修改,因为Marc指出这应该是带有复制值的新对象,而不仅仅是对同一对象的新引用。
答案 1 :(得分:2)
您已在information
上修改了关闭。
答案 2 :(得分:2)
我认为这是因为您在单个外部ExchangeEmailInformation
循环迭代中对每个内部information
循环迭代使用相同的foreach
实例foreach
;在上一次调用有机会读取其值之前,您可以更新该实例中的属性以进行下一次异步调用。
在异步情况下,事件的顺序是
information
以进行通话1 information
以进行通话2 因此,当执行调用1时,information
已经包含调用2的数据。在同步情况下,这不会发生;在调用1执行完毕之前,循环无法继续。
我认为解决此问题的最佳方法是停止更改 information
并将三个用于更改的字段作为单独的参数传递。 (实际上看起来UserAlias
只需要更新一次,因此您不需要单独传递它。请注意,如果您这样做,则无需复制queueList
。)
ExchangeEmailInformation information = exchangeEmailInformation;
var emailId = information.Sender.Split('@');
information.UserAlias = emailId[0];
foreach (var queueList in exchangeEmailInformation.Attachment)
{
AddAttachmentAsync(information, queueList.Name, queueList.Size);
}
// and modify AddAttachmentAsync to use these two parameters too
另一种方法是拍摄queueList
的副本,然后将information
和该副本传递到AddAttachmentAsync
并拉出适当的两个参数:
ExchangeEmailInformation information = exchangeEmailInformation;
var emailId = information.Sender.Split('@');
information.UserAlias = emailId[0];
foreach (var queueList in exchangeEmailInformation.Attachment)
{
var attachment = queueList;
AddAttachmentAsync(information, attachment);
}
// and modify AddAttachmentAsync to pull properties from the right parameter.
答案 3 :(得分:1)
在第二个foreach()的第一行中,您必须复制“信息”并将副本传递给AddAttachmentAsync。 (即,所有数据的副本 - 而不仅仅是对象引用的副本。)
正在发生的事情是,在AddAttachmentAsync()返回之前,您传递给AddAttachmentAsync()的'information'对象正在被更改。
通常,在设计用于多线程内容的类(如ExchangeEmailInformation)时,应该使它们成为不可变的 - 然后就不可能发生这种特殊的事情。 (在我看来,你应该使所有POD(“普通旧数据”)类不可变。)
答案 4 :(得分:0)
除了正确评估您需要为每个线程创建information
实例的其他答案之外,我还会提出另一个建议。我注意到你创建了一个调用AddAttachment
,虽然目前它只是一个Console.Write
我想你最终会访问一个列表并将结果实例添加到这个列表中。这将创建访问共享资源的不必要情况。
让主线程创建具有引用类型属性的对象的实例并将其传递给线程,而不是让每个线程负责将其结果添加到列表中。这样,线程已经有了存储结果的位置,因为它不是共享资源,所以你不必担心同步。
这是一个非常基本的例子。显然你必须根据自己的需要略微改变它。
class Result
{
public object Data { get; set; }
}
List<Work> WorkToDo = // Some population call
List<Result> Results = new List<Result>();
foreach (Work Item in WorkToDo)
{
Result Result = new Result();
Results.Add(Result);
System.Threading.Tasks.Task.Factory.StartNew(
() => { Result.Data = "Hello World."; });
}
同样,这只是为了指出,仅仅因为您希望将所有结果编译成某种列表或数组并不意味着您需要共享对列表的访问权限。让父线程处理所有这些可以大大简化你的算法。