我有一个应用程序,我在其中登录 azure appendblob 作为csv。我使用LinqToCsv-> CsvContext写为csv。
每次我写日志,我检查appendblob是否有任何长度,如果它是零,那么我将标题写入csv。 在我以多线程方式进行测试之前,每件事情都工作正常。因此,不会在csv中多次写入标题。
我使用 lock 语句阻止多个线程访问语句块,但它没有按预期工作。
请让我知道我在这里做错了什么。
这是我的代码:
public async Task WriteToAudit(AuditData auditData)
{
_auditBlobName = Utilities.GetAuditAppendBlobName(auditData.Container);
var appendBlob = await GetAppendBlobReferenceAsync();
var list = new List<AuditData> { auditData };
var auditDataBytes = CloudAppendBlobHelper.WriteCsvWithHeaderToMemory(list, appendBlob);
using (var stream = new MemoryStream(auditDataBytes))
{
await appendBlob.AppendBlockAsync(stream).ConfigureAwait(false);
}
}
public static class CloudAppendBlobHelper
{
private static readonly object syncLock = new object();
public static byte[] WriteCsvWithHeaderToMemory(IEnumerable<AuditData> records, CloudAppendBlob appendBlob)
{
lock (syncLock)
{
appendBlob.FetchAttributes();
var outputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
EnforceCsvColumnAttribute = true,
FirstLineHasColumnNames = appendBlob.Properties.Length <= 0
};
using (var memoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(memoryStream))
{
var context = new CsvContext();
context.Write(records, streamWriter, outputFileDescription);
}
return memoryStream.ToArray();
}
}
}
}
答案 0 :(得分:1)
您正在呼叫&#39;获取属性&#39;在一个锁内,但你也不会在锁中调用AppendBlock API。这引入了竞争条件,其中可以执行以下操作顺序:
一种解决方案是将AppendBlock调用放在锁内。
如果要保持并行性,另一种解决方案是使用AccessCondition。在编写标题时,可以使用&#34; ifAppendPositionEqual&#34;条件,或者&#34; ifMaxSizeLessThanOrEqual&#34;条件,例如。如果不满足条件,这些将导致写操作在服务上失败。然后,您需要捕获失败,并重做没有标题的追加操作。如果你这样做,我认为你应该能够完全移除锁。
请注意,您在AppendBlock调用时需要知道您是否正在编写标题。