首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >并发字典并行读取和删除项

并发字典并行读取和删除项
EN

Stack Overflow用户
提问于 2022-10-10 21:23:17
回答 1查看 65关注 0票数 0

问题背景:

目前,我正在学习如何使用the.NET 6 Parallel.ForEachAsync循环进行并发操作。

我已经创建了下面的程序,它具有两个任务在Task.WhenAll函数中并行运行。KeepAliveAsync方法在HttpClient调用中运行最多3级的并行性,总共包含10项。DisposeAsync方法将从并发字典中删除这些项,但在此之前,CleanItemBeforeDisposal方法将删除项对象的属性值。

代码:

代码语言:javascript
复制
{
    [TestClass]
    public class DisposeTests
    {
        private ConcurrentDictionary<int, Item> items = new ConcurrentDictionary<int, Item>();

        private bool keepAlive = true;

        [TestMethod]
        public async Task Test()
        {
            //Arrange
            string uri = "https://website.com";

            IEnumerable<int> itemsToAdd = Enumerable.Range(1, 10);
            IEnumerable<int> itemsToDispose = Enumerable.Range(1, 10);

            foreach (var itemToAdd in itemsToAdd)
            {
                items.TryAdd(itemToAdd, new Item { Uri = uri });
            }

            //Act
            await Task.WhenAll(KeepAliveAsync(), DisposeAsync(itemsToDispose));

            //Assert
            Assert.IsTrue(items.Count == 0);
        }

        private async Task KeepAliveAsync()
        {
            HttpClient httpClient = new HttpClient();

            do
            {
                ParallelOptions parallelOptions = new()
                {
                    MaxDegreeOfParallelism = 3,
                };

                await Parallel.ForEachAsync(items.ToArray(), parallelOptions, async (item, token) =>
                {
                    var response = await httpClient.GetStringAsync(item.Value.Uri);

                    item.Value.DataResponse = response;

                    item.Value.DataResponse.ToUpper();
                });

            } while (keepAlive == true);
        }

        private async Task DisposeAsync(IEnumerable<int> itemsToRemove)
        {
            var itemsToDisposeFiltered = items.ToList().FindAll(a => itemsToRemove.Contains(a.Key));

            ParallelOptions parallelOptions = new()
            {
                MaxDegreeOfParallelism = 3,
            };

            await Parallel.ForEachAsync(itemsToDisposeFiltered.ToArray(), parallelOptions, async (itemsToDispose, token) =>
            {
                await Task.Delay(500);

                CleanItemBeforeDisposal(itemsToDispose);

                bool removed = items.TryRemove(itemsToDispose);

                if (removed == true)
                {
                    Debug.WriteLine($"DisposeAsync - Removed item {itemsToDispose.Key} from the list");
                }
                else
                {
                    Debug.WriteLine($"DisposeAsync - Did not remove item {itemsToDispose.Key} from the list");
                }
            });

            keepAlive = false;
        }

        private void CleanItemBeforeDisposal(KeyValuePair<int, Item> itemToDispose)
        {
            itemToDispose.Value.Uri = null;
            itemToDispose.Value.DataResponse = null;
        }
    }
}

问题:

代码运行,但我遇到了一个问题,即Item对象的Uri属性在CleanItemBeforeDisposal方法中被设置为null,设计时从Dispose方法调用,但是在并行KeepAliveAsync方法中进行HttpClient调用,此时共享项对象为null,错误有:

代码语言:javascript
复制
System.InvalidOperationException: An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set.

我在共享的ToArray上使用了ConcurrentDictionary方法,因为我相信这将在调用字典时创建字典的快照,但显然这不能解决这个争用条件。

如何处理两个进程访问一个共享列表的情况,其中一个进程更改了另一个进程所需的该列表实体的属性?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-10-11 17:57:24

我将尽量直接回答这个问题,而不深入了解设计等细节。

ConcurrentDictionary是线程安全的,这意味着多个线程可以安全地从字典中添加和删除项。这种线程安全性根本不适用于字典中存储为值的任何对象。

如果多个线程具有对Item实例的引用并正在更新其属性,则可能会发生各种不可预测的事情。

为了直接回答这个问题:

如何处理两个进程访问一个共享列表的情况,其中一个进程可能已经更改了另一个进程所需的该列表实体的属性?

没有正确的方法来处理这种可能性。如果希望代码以可预测的方式工作,则必须消除这种可能性。

看起来,您可能希望这两个操作能够保持同步。他们不会的。即使他们这样做了,只要一次,它可能永远不会再次发生。这是不可预测的。

如果您实际上需要将UriResponse设置为null,那么最好在使用这些值之后立即对同一个线程中的每个Item执行该操作。如果你做这三件事

执行对单个Item

  • Do的请求,值

  • 将其设置为null

在同一个线程中,...one接二连三地出现,它们不可能出现故障。

(但您需要将它们设置为空吗?)还不清楚那是干什么用的。如果你不这么做,就不会有什么问题需要解决。)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74020797

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档