c#等待任务-局部变量意外更改
我刚刚开始在我正在编写的一些方法中实现异步编程 业务逻辑是接受入站调用,处理一个项目(如果它不在全局缓存中),或者如果它在全局缓存中,只需更新条目,然后在代码中稍后处理该条目 然而,我看到一些奇怪的行为,我不能左右我的头。我的单元测试向我的QueueProcessor方法提交两个请求(每次调用之间有2秒的延迟)。为了进行测试,我特意在QueueProcessor处理请求时调用的另一个方法中使用了task.delay。这模拟了一个真实的测试用例,在第一个请求被处理时,我们会保留额外的请求c#等待任务-局部变量意外更改,c#,asp.net,asynchronous,asp.net-web-api,C#,Asp.net,Asynchronous,Asp.net Web Api,我刚刚开始在我正在编写的一些方法中实现异步编程 业务逻辑是接受入站调用,处理一个项目(如果它不在全局缓存中),或者如果它在全局缓存中,只需更新条目,然后在代码中稍后处理该条目 然而,我看到一些奇怪的行为,我不能左右我的头。我的单元测试向我的QueueProcessor方法提交两个请求(每次调用之间有2秒的延迟)。为了进行测试,我特意在QueueProcessor处理请求时调用的另一个方法中使用了task.delay。这模拟了一个真实的测试用例,在第一个请求被处理时,我们会保留额外的请求 我在名为
我在名为ProcessRoutine的子方法的作用域中使用了一个局部变量。但由于某种原因,当第二个调用用于更新全局缓存时,仅在ProcessRoutine方法范围内的局部变量也会发生更改 此外,第二个请求将只更新全局缓存变量然后停止的操作逻辑。因此,不会触发其他代码。我已经通过日志证实了这一点。我就是不明白为什么ProessRoutine方法中传递的数据集可以这样更改
public async Task<bool> QueueProcessor(RtcPluginModel_IncidentModel passInModel)
{
//Ensure the processing cache is instantiated
if (QueueGlobalVariables.processingCache == null)
{
QueueGlobalVariables.processingCache = new List<tempTicketData>();
}
try
{
tempTicketData ticketItem = (tempTicketData)passInModel;
ticketItem.timeStamp = DateTime.Now;
var checkItemExistsInProcessingCache =
QueueGlobalVariables.processingCache.Find(
x => x.PAName == passInModel.PAName);
if (checkItemExistsInProcessingCache != null)
{
var result = QueueGlobalVariables.processingCache.Remove( QueueGlobalVariables.processingCache.Find(
x => x.PAName == passInModel.PAName && x.recordId == passInModel.recordId));
QueueGlobalVariables.processingCache.Add(ticketItem);
logger.Trace("Stopping update branch of code as no further action needed at this point.");
}
else
{
QueueGlobalVariables.processingCache.Add(ticketItem);
do
{
var cycleTickets = QueueGlobalVariables.processingCache.Find(
x => x.PAName == passInModel.PAName);
var task = Task.Run(() => ProcessRoutineAsync(cycleTickets));
await task;
} while (QueueGlobalVariables.processingCache.Find(
x => x.PAName == passInModel.PAName) != null);
}
}
catch (Exception e)
{
logger.Trace("An exception has occured in the queue handler class: " + e.Message);
}
return true;
}
****更新
我已经找到了一个解决问题的方法,但我仍然不明白为什么会发生这种情况:o/
我修改了QueueProcessor,使另一个变量独占地保存时间戳:
var ticketInstance = passInModel;
var saveTimeIndex = ticketInstance.timeStamp;
然后,我将新变量传递给if语句,该语句将刚刚处理的票证的时间戳与缓存中的内容进行比较。现在一切正常。但是首先肯定不需要这个新变量吗?
EnterticketData
是一个类。在C#中,所有class
e都是引用类型。所有指向类实例的引用都指向数据的一个副本。当您更改此通用副本时,每个人都会看到更改(最终,多线程是很难的)
当您执行(诱惑icketdata)passInModel
时,不会复制票据,它仍然是对同一票据的引用。因此,当您稍后修改它时,您将修改缓存中的票证,而不仅仅是本地票证-本地票证只是对缓存中对象的引用,而不是对象的副本
这是一件大事。确保您完全理解这是如何工作的——这是编程的基础之一,也是理解编程的主要障碍之一
一般来说,这是一个间接的问题。让我们想象一个简单的虚拟机,它代表您的(简化的)程序
您有一个enticketdata
对象,一张纸,存储在冰箱中的C42框中。在这张纸上,你写了Hi代码>。您的缓存对象有一个便利贴,上面写着“我的对象存储在冰箱中,在C42框中”。现在,当你从缓存中读取票证时,你所读的只是便笺——因为C#的工作方式,它还允许你访问真实实例的所有成员。因此,当你使用诸如Console.WriteLine(ticket.timeStamp)
之类的代码时,C#会看一下邮件,走到冰箱前,给你读报纸
现在,当你制作你的本地,ticketItem
,你复制了post-it,它还写着“我的物品储存在冰箱里,在C42框中”。当您更改ticketItem.timeStamp
时,您将进入冰箱,进入C42框,并更换纸张。毫不奇怪,当有人读到原始的post-it并走到冰箱前时,他也会看到你的更改-只有一个EnterticketData
对象。多张便笺指向冰箱中的同一位置这一事实没有帮助——只有一张票据。enticketdata
是一个类别。在C#中,所有class
e都是引用类型。所有指向类实例的引用都指向数据的一个副本。当您更改此通用副本时,每个人都会看到更改(最终,多线程是很难的)
当您执行(诱惑icketdata)passInModel
时,不会复制票据,它仍然是对同一票据的引用。因此,当您稍后修改它时,您将修改缓存中的票证,而不仅仅是本地票证-本地票证只是对缓存中对象的引用,而不是对象的副本
这是一件大事。确保您完全理解这是如何工作的——这是编程的基础之一,也是理解编程的主要障碍之一
一般来说,这是一个间接的问题。让我们想象一个简单的虚拟机,它代表您的(简化的)程序
您有一个enticketdata
对象,一张纸,存储在冰箱中的C42框中。在这张纸上,你写了Hi代码>。您的缓存对象有一个便利贴,上面写着“我的对象存储在冰箱中,在C42框中”。现在,当你从缓存中读取票证时,你所读的只是便笺——因为C#的工作方式,它还允许你访问真实实例的所有成员。因此,当你使用诸如Console.WriteLine(ticket.timeStamp)
之类的代码时,C#会看一下邮件,走到冰箱前,给你读报纸
现在,当你制作你的本地,ticketItem
,你复制了post-it,它还写着“我的物品储存在冰箱里,在C42框中”。当您更改ticketItem.timeStamp
时,您将进入冰箱,进入C42框,并更换纸张。毫不奇怪,当有人读到原始的post-it并走到冰箱前时,他也会看到你的更改-只有一个EnterticketData
对象。多张便笺指向冰箱中的同一位置这一事实没有帮助,因为只有一张便笺。是否使用相同的参数连续调用QueueProcessor()方法两次?在
[XmlInclude(typeof(tempTicketData))]
public class MyTicketModel
{
public string recordId { get; set; }
public string PAName { get; set; }
public string ItemA { get; set; }
public string ItbmB { get; set; }
}
[XmlInclude(typeof(tempTicketData))]
public class tempTicketData : MyTicketModel
{
public DateTime timeStamp { get; set; }
}
var ticketInstance = passInModel;
var saveTimeIndex = ticketInstance.timeStamp;