Home > Blockchain >  Selective thread to avoid sharing an object
Selective thread to avoid sharing an object

Time:09-23

The code below is simplified. There is a massive method which iterates many times. The threaded method receives an element of a List<Class>.

Because the threaded method modifies an element which is an object in the method, I do not want that the same argument is loaded on the separate threads concurrently. Because each element is independent, I want that the method with each one runs concurrently.

How to run the method with the same argument sequentially and run the method with a different argument concurrently?

Do I have to verify each running thread one by one before New & Start the method, whether there is the method with the same argument or not?

class Class1
{
    // something
}

private void Form1_Load(object sender, EventArgs e)
{
    List<Class1> _List = new();
            
    for (int i = 0; i < 10; i  )
    {
        _List.Add(new Class1 { });
    }
            
    for (int i = 0; i < 10; i  )
    {
        Thread _Thread = new(Method1);

        _Thread.Start(_List[new Random().Next(10)]); // the argument can be consecutively same element of List1
    }
}

void Method1(object _Object)
{
    // modifies _Object
}

CodePudding user response:

  1. If a random order is really required, randomize the list before using it: Randomize a List<T>

  2. Creating new threads is expensive, prefer ThreadPool

  3. To process a list of items in parallel, use Parallel.ForEach(_List, Method1)

CodePudding user response:

You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:

void Main()
{
    var foos = new [] { new Foo("A"), new Foo("B"), new Foo("C"), new Foo("D") };
    
    var query =
        from n in Observable.Range(0, 1000).Select(n => __random.Next(foos.Length))
        group foos[n] by foos[n].Name into grouped_foos
        from gf in grouped_foos.ObserveOn(Scheduler.Default)
        select new { Foo = gf, ManagedThreadId = Method1(gf) };

    query
        .ToArray()
        .Select(x => x.GroupBy(y => (y.Foo, y.ManagedThreadId)).Select(y => (y.Key.Foo, y.Key.ManagedThreadId, Count: y.Count())))
        .Subscribe(xs =>
            Console.WriteLine(
                String.Join(
                    Environment.NewLine,
                    xs.Select(x => $"{x.Count} x {x.Foo.Name} {x.Foo.Counter} run on thread {x.ManagedThreadId}"))));
}

int Method1(Foo foo)
{
    foo.Counter = foo.Counter   1;
    return System.Threading.Thread.CurrentThread.ManagedThreadId;
}


private static Random __random = new Random();

public class Foo
{
    public int Counter { get; set; } = 0;
    public string Name { init; get; }
    public Foo(string name)
    {
        this.Name = name;
    }
    public override int GetHashCode() => this.Name.GetHashCode();
    public override bool Equals(object obj) => obj is Foo f ? this.Name.Equals(f.Name) : false;
}

When I run that I get this typical output:

259 x A 259 run on thread 38
239 x D 239 run on thread 14
238 x C 238 run on thread 11
264 x B 264 run on thread 36

The code is creating a sequence of 1_000 indices randomly selecting a Foo from 4 distinct instances. It then groups by those instances and performs some processing. The processing returns the managed thread id to see which thread ran which code.

  • Related