ConcurrentDictionary 缺失数值?

huangapple go评论69阅读模式
英文:

ConcurrentDictionary missing values?

问题

我有以下代码

ConcurrentDictionary<string, XElement> DocumentsElement = new ConcurrentDictionary<string, XElement>();

public void AddDocument(string docId)
{
    bool added;
    try
    {
        var documentElement = new XElement("Document", new XAttribute("DocID", docId));

        lock (lockObject)
        {
            added = DocumentsElement.TryAdd(docId, documentElement);

            string path = $"{_batchNumber}";
            if(!Directory.Exists(path)) { Directory.CreateDirectory(path); }
            File.AppendAllText($"{path}/{docId}.txt", documentElement.ToString());

        }
    }
    catch (Exception ex)
    {
        added = false;
    }

    if (added)
    {
        Debug.WriteLine("Document added successfully.");
    }
    else
    {
        Debug.WriteLine("Failed to add document.");
    }

    lock (lockObject)
    {
        Debug.WriteLine("From AddDocument method " + DocumentsElement.Count);
    }
}

问题是在整个过程结束时,DocumentsElement 缺少一些值,然而 "File.AppendAllText" 正确地将 XElement 值写入文本文件,所有文件都存在。有多个线程访问此 AddDocument 方法。

例如,DocumentsElement.Count = 4495
文本文件数量 = 4500(正确数量)。

我是否忽略了一些同步问题?

调用代码在我的主应用程序中,Process(ConcurrentDictionary<long, byte> idList)

private ConcurrentDictionary<string, DocumentLoadFile> loadfiles;

private async Task Process(ConcurrentDictionary<long, byte> idList)
{
     var tasks = idList.Select(async (id) =>
     {
          var metadata = FetchMetadataForId(id);
          var loadfile = loadfiles[metadata.BatchId];
          
          loadfile.AddDocument(metadata.DocumentId);

          if(metadata.HasAttachment)
              ProcessAttachment(metadata.AttachmentId)
     });

     await Task.WhenAll(tasks);

     //dostuff
}


private void ProcessAttachment(long attId)
{
          var metadata = FetchMetadataForId(attId);
          var loadfile = loadfiles[metadata.BatchId];
          
          loadfile.AddDocument(metadata.DocumentId);

}
英文:

I have the following code

ConcurrentDictionary&lt;string, XElement&gt; DocumentsElement = new ConcurrentDictionary&lt;string, XElement&gt;();

        public void AddDocument(string docId)
        {
            bool added;
            try
            {
                var documentElement = new XElement(&quot;Document&quot;, new XAttribute(&quot;DocID&quot;, docId));

                lock (lockObject)
                {
                    added = DocumentsElement.TryAdd(docId, documentElement);

                    string path = $&quot;{_batchNumber}&quot;;
                    if(!Directory.Exists(path)) { Directory.CreateDirectory(path); }
                    File.AppendAllText($&quot;{path}/{docId}.txt&quot;, documentElement.ToString());

                }
            }
            catch (Exception ex)
            {
                added = false;
            }

            if (added)
            {
                Debug.WriteLine(&quot;Document added successfully.&quot;);
            }
            else
            {
                Debug.WriteLine(&quot;Failed to add document.&quot;);
            }

            lock (lockObject)
            {
                Debug.WriteLine(&quot;From AddDocument method &quot; + DocumentsElement.Count);
            }
        }

The issue is that at the end of the entire process, DocumentsElement is missing some values., however the "File.AppendAllText" is correctly writing the XElement value into a text file and all files are there. There are multiple threads that access this AddDocument method.

e.g. DocumentsElement.Count = 4495
No. Text files = 4500 (correct count).

Is there some synchronization issue that I'm missing?

The calling code is in my main app, Process(ConcurrentDictionary<long, byte> idList)

private ConcurrentDictionary&lt;string, DocumentLoadFile&gt; loadfiles;

private async Task Process(ConcurrentDictionary&lt;long, byte&gt; idList)
{
     var tasks = idList.Select(async (id) =&gt;
     {
          var metadata = FetchMetadataForId(id);
          var loadfile = loadfiles[metadata.BatchId];
          
          loadfile.AddDocument(metadata.DocumentId);

          if(metadata.HasAttachment)
              ProcessAttachment(metadata.AttachmentId)
     });

     await Task.WhenAll(tasks);

     //dostuff
}


private void ProcessAttachment(long attId)
{
          var metadata = FetchMetadataForId(attId);
          var loadfile = loadfiles[metadata.BatchId];
          
          loadfile.AddDocument(metadata.DocumentId);

}



</details>


# 答案1
**得分**: 2

你正在向文件 "unconditionally" 添加内容,而字典会跳过重复项。根据 [`ConcurrentDictionary&lt;TKey, TValue&gt;.TryAdd(TKey, TValue)`][1] 文档:

> 尝试将指定的键和值添加到 `ConcurrentDictionary&lt;TKey, TValue&gt;` 中。
> 如果成功将键/值对添加到 `ConcurrentDictionary&lt;TKey, TValue&gt;`,则返回 `true`;如果键已经存在,则返回 `false`。

只有在字典中不存在该项时才尝试添加到文件中:

```csharp
var documentElement = new XElement("Document", new XAttribute("DocID", docId));
added = DocumentsElement.TryAdd(docId, documentElement);
if (added)
{
    lock (lockObject)
    {
        string path = $"{_batchNumber}";
        if (!Directory.Exists(path)) { Directory.CreateDirectory(path); }
        File.AppendAllText($"{path}/{docId}.txt", documentElement.ToString());
    }
}
英文:

You are adding to the file "unconditionally", while dictionary will skip duplicates. From the ConcurrentDictionary&lt;TKey,TValue&gt;.TryAdd(TKey, TValue) docs:

> Attempts to add the specified key and value to the ConcurrentDictionary&lt;TKey,TValue&gt;.
> Returns true if the key/value pair was added to the ConcurrentDictionary&lt;TKey,TValue&gt; successfully; false if the key already exists.

Try adding to the file only in case if the item is not present in the dictionary:

var documentElement = new XElement(&quot;Document&quot;, new XAttribute(&quot;DocID&quot;, docId));
added = DocumentsElement.TryAdd(docId, documentElement);
if (added)
{
    lock (lockObject)
    {
        string path = $&quot;{_batchNumber}&quot;;
        if(!Directory.Exists(path)) { Directory.CreateDirectory(path); }
        File.AppendAllText($&quot;{path}/{docId}.txt&quot;, documentElement.ToString());
    }
}

答案2

得分: 1

首先建议您进行以下更改(并理解),这将使代码更加简洁,并在存在重复项时提供通知:

```csharp
public void AddDocument(string docId)
{
    try
    {
        lock (lockObject)
        {
            var documentElement = new XElement("Document", new XAttribute("DocID", docId));
            if (!DocumentsElement.TryAdd(docId, documentElement))
                Debug.WriteLine("重复项:{0}", docId);

            // 文件I/O较慢。您可以考虑将其外包给队列并并行执行。
            string path = $"{_batchNumber}";
            if(!Directory.Exists(path)) { Directory.CreateDirectory(path); }
            File.AppendAllText($"{path}/{docId}.txt", documentElement.ToString());
            Debug.WriteLine("成功添加文档。");
            Debug.WriteLine("来自AddDocument方法的文档计数:" + DocumentsElement.Count);
        }
    }
    catch (Exception ex)
    {
         Debug.WriteLine("添加文档失败。");
    }
}
英文:

For a first approach, I'd recommend to make (and understand) these changes, which makes this a little smaller and tells you when there are dupes:

public void AddDocument(string docId)
{
    try
    {
        lock (lockObject)
        {
            var documentElement = new XElement(&quot;Document&quot;, new XAttribute(&quot;DocID&quot;, docId));
            if (!DocumentsElement.TryAdd(docId, documentElement))
                Debug.WriteLine(&quot;Duplicate : {0}&quot;, docId);

            // File I/O is slow. You may consider outsourcing this
            // to a queue and execute in parallel.
            string path = $&quot;{_batchNumber}&quot;;
            if(!Directory.Exists(path)) { Directory.CreateDirectory(path); }
            File.AppendAllText($&quot;{path}/{docId}.txt&quot;, documentElement.ToString());
            Debug.WriteLine(&quot;Document added successfully.&quot;);
            Debug.WriteLine(&quot;From AddDocument method &quot; + DocumentsElement.Count);
        }
    }
    catch (Exception ex)
    {
         Debug.WriteLine(&quot;Failed to add document.&quot;);
    }
}

</details>



huangapple
  • 本文由 发表于 2023年7月10日 13:12:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76650805.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定