HttpClient μ—λŸ¬?

μœ λ‹› ν…ŒμŠ€νŠΈλ₯Ό ν•˜λ‹€κ°€ μ•„λž˜μ™€ 같은 μ˜ˆμ™Έκ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.

Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection’s state is no longer correct.

κ΄€λ ¨ν•΄μ„œ ꡬ글에 κ²€μƒ‰ν•΄λ³΄λ‹ˆ 원인이 λͺ…ν™•ν•˜μ§€ μ•Šμ€ 것 κ°™μŠ΅λ‹ˆλ‹€.
λ¬Έμ œκ°€ λ°œμƒν•œ μœ λ‹›ν…ŒμŠ€νŠΈ λ©”μ„œλ“œλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. NUnit을 톡해 μž‘μ„±λ˜μ—ˆκ³ , μ²˜μŒλ³΄λŠ” ν΄λž˜μŠ€λŠ” 자체 μ œμž‘ 클래슀라고 λ³΄μ‹œλ©΄ 될 λ“― ν•©λ‹ˆλ‹€.


        [TestCase(419364050, EShopeeItemStatus.NORMAL)]
        [TestCase(419364050, EShopeeItemStatus.UNLIST)]
        public async Task InitGmarketWhiteList(long shopId, EShopeeItemStatus eShopeeItemStatus)
        {
            RequestGetItemList req = new()
            {
                ShopId = shopId,
                PartnerId = _shopeeOption.PartnerId,
                Offset = 0,
                PageSize = 100,
                UpdateTimeFrom = new DateTimeOffset(DateTime.Now.AddYears(-3)).ToUnixTimeSeconds(),
                UpdateTimeTo = new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds(),
                ItemStatus = eShopeeItemStatus
            };
            TaskBase<ResponseGetItemBaseInfo> taskBaseRes = await req.FullBaseInfoScanAsync(_shopeeClient);

            if (!taskBaseRes.IsSuccess)
            {
                Assert.False(!taskBaseRes.IsSuccess);
            }

            ResponseGetItemBaseInfo res = taskBaseRes.Result;
            AwesomeSwagClient awesomeSwagClient = new(new HttpClient(), _companyId);
            SemaphoreSlim semaphore = new(20);
            List<Task> tasks = new();

            foreach (ResponseGetItemBaseInfo._Item item in res.ItemList)
            {
                tasks.Add(Task.Run(async () =>
                {
                    await semaphore.WaitAsync();

                    try
                    {
                        Product awesomeSwagSourceItem = await awesomeSwagClient.GetProductFromOrgIdDetailAsync(item.ItemSku);

                        if (awesomeSwagSourceItem is null)
                        {
                            EcWhiteList reqWhiteList = new()
                            {
                                Platform = EPlatform.SHOPEE,
                                PlatformId = item.ItemId.ToString(),
                                ShopId = shopId.ToString(),
                                ShopName = string.Empty,
                            };
                            await _ecWhiteListService.InsertAsync(reqWhiteList); // μ˜ˆμ™Έ λ°œμƒμ§€μ 
                        }
                    }
                    finally
                    {
                        semaphore.Release();
                    }
                }));
            }

            await Task.WhenAll(tasks);

            Assert.True(true);
        }

        public async Task InsertAsync(EcWhiteList edata)
        {
            try
            {
                _context.EcWhiteLists.Add(edata);

                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
            }
            
        }

λ¬Έμ œκ°€ λ°œμƒν•œ λ©”μ„œλ“œλŠ” μ£Όμ„μœΌλ‘œ ν‘œκΈ°ν–ˆκ³  λ©”μ„œλ“œμ˜ μ •μ˜λŠ” μ•„λž˜ InsertAsync λ©”μ„œλ“œμž…λ‹ˆλ‹€.
Cosmos DB에 Semaphoreλ₯Ό ν™œμš©ν•˜μ—¬ μ›Ήν†΅μ‹ ν•œ κ²°κ³Όλ₯Ό μ €μž₯ν•˜λŠ” μ½”λ“œμΈλ° 이런 μ—λŸ¬κ°€ λ°œμƒν•©λ‹ˆλ‹€β€¦
λ¬Όλ‘  λ³‘λ ¬μ²˜λ¦¬μ—†μ΄ λ™κΈ°μ‹μœΌλ‘œ foreachλ¬Έμ—μ„œ ν•˜λ‚˜μ”© 돌리면 λ¬Έμ œκ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ™œ λ°œμƒν•˜λŠ” κ±ΈκΉŒμš”?

2개의 μ’‹μ•„μš”

DbContext의 μŠ€λ ˆλ”© 문제둜 λ³΄μž…λ‹ˆλ‹€.

5개의 μ’‹μ•„μš”

@dimohy λ‹˜κ³Ό 같은 μ˜κ²¬μž…λ‹ˆλ‹€.

EntityFrameworkλŠ” Thread-safety둜 λ””μžμΈλ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

SemaphoreSlim을 μƒμ„±ν• λ•Œ μƒμ„±μžμ˜ 인자둜 (1, 1)을 λ„£μ–΄λ³΄μ„Έμš”.
μ•„λ§ˆ ν•΄λ‹Ή μž₯μ• κ°€ μ—†μ–΄μ§ˆκ±° κ°™μŠ΅λ‹ˆλ‹€.

4개의 μ’‹μ•„μš”

μ•„ν•˜β€¦κ·ΈλŸΌ AddRange ν˜•νƒœλ‘œ ν•΄μ•Όκ² λ„€μš”β€¦γ…Žγ…Ž κ°μ‚¬ν•©λ‹ˆλ‹€!!

1개의 μ’‹μ•„μš”

λ‹΅λ³€ κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€!! μ½”λ“œ μˆ˜μ •ν•΄μ•Όκ² λ„€μš” γ…Žγ…Ž

1개의 μ’‹μ•„μš”

EF의 νƒˆμ„ μ“΄, λ‹¨μˆœν•œ μ»¬λ ‰μ…˜μ˜ thread-not-safe λ¬Έμ œμž…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λ‹€μŒκ³Ό 같은 μ‹μœΌλ‘œλ„ κ°„λ‹¨ν•˜κ²Œ μž¬ν˜„μ„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

static async Task Main(string[] args)
{
    Dictionary<int, int> dict = new Dictionary<int, int>(1000000);
    List<Task> tasks = new List<Task>();

    for (int i = 0; i < 2; i++)
    {
        Task task = Task.Run(() => 
        {
            for (int j = 0; j < 1000000; j++)
            {
                dict[j] = j;
            }
        });
        tasks.Add(task);
    }

    await Task.WhenAll(tasks);
}
6개의 μ’‹μ•„μš”

예제 μ½”λ“œ κ°μ‚¬ν•©λ‹ˆλ‹€ γ…Žγ…Ž 동기화가 μ²˜λ¦¬λ˜μ§€ μ•Šμ€ μ»¬λ ‰μ…˜ 객체에 λŒ€ν•΄μ„œλŠ” λ©€ν‹°μŠ€λ ˆλ”©ν•˜λ©΄μ„œ μˆ˜μ •ν•˜λ©΄ μ•ˆλœλ‹€λŠ” κ²ƒμ΄κ΅°μš”.

2개의 μ’‹μ•„μš”