1

My code like that.

List<MyPanel> list_panel = new List<MyPanel>();
.......
List<string> list_sql = new List<string>();
Parallel.For(0, list_panel.Count, i =>
{
    if (list_panel[i].R == 0)
    {
        list_sql.AddRange(list_panel[i].MakeSqlForSave()); // it returns two string
    }
});

But AddRange occur System.ArgumentException sometimes.

I found 'list isn't for multi write'. So I fix it using lock.

string[] listLock = new string[2];
Parallel.For(0, list_panel.Count, i =>
{
    if (list_panel[i].R == 0)
    {
        listLock = list_panel[i].MakeSqlForSave();    
        lock(listLock)
            list_sql.AddRange(listLock);
    }
});

But it still occur System.ArgumentException that 'Source array was not long enough. Check srcIndex and length, and the array's lower bounds.' sometimes.

An error occurred in list_sql. If the count is 34, but the IndexOfRangeException occurs when you call list_sql[32] and list_sql[33].

How can I handle it?

4
  • 2
    List<T> (and especially List<T>.AddRange) is not thread-safe. Commented Jan 2, 2020 at 2:24
  • 1
    Your lock() statement is incorrect because it locks on a thread-local object rather than a shared object. Commented Jan 2, 2020 at 2:25
  • 1
    Is MyPanel a WinForms control? If so, this code is unsafe because WinForms controls should only be accessed from the GUI thread. Commented Jan 2, 2020 at 2:29
  • You reassign listlock, then lock on it. This makes the lock meaningless as you will have many locks. You don't lock on the variable, you lock on the (new) object it refers to. Commented Jan 2, 2020 at 13:17

2 Answers 2

3

Use ConcurrentBag<T> as a thread-safe collection that you can safely append to from multiple threads:

ConcurrentBag<String> result = new ConcurrentBag<String>();

Parallel.For(0, list_panel.Count, i =>
{
    if (list_panel[i].R == 0)
    {
        foreach( String s in list_panel[i].MakeSqlForSave() )
        {
            result.Add( s );
        }
    }
});

List<String> list_sql = result.Select( s => s ).ToList(); // Serialize to a single List<T> after the concurrent operations are complete.
Sign up to request clarification or add additional context in comments.

Comments

1

You must use a dedicated lock for the specific List, and use it every time you access this list (for both read and write).

List<MyPanel> list_panel = new List<MyPanel>();
List<string> list_sql = new List<string>();
object listSqlLock = new object();
Parallel.For(0, list_panel.Count, i =>
{
    if (list_panel[i].R == 0)
    {
        var sqlCommands = list_panel[i].MakeSqlForSave();
        lock (listSqlLock)
            list_sql.AddRange(sqlCommands);
    }
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.