简体   繁体   中英

Share method between threads

I have a Func like this:

int loopMax = 10, taskMax = 10;
int executionCounter = 0;

Func<int> calculator = new Func<int>(() =>
{
    executionCounter++;
    int result = 0;
    for (int i = 0; i < loopMax; i++)
    {
        Thread.Sleep(100);
        if (result + i >= int.MaxValue)
            result = 0;
        result += i;
    }
    return result;
});

Which could be called by multiple threads. For example like this:

Task[] tasks = new Task[taskMax];
for (int i = 0; i < taskMax; i++)
{
    tasks[i] = Task.Run(() => _=calculator());
}
Task.WaitAll(tasks);

I need to share the calculator function across all threads, and makes this function being called only once. In fact the executionCounter variable's value after running this code should remain 1, and all of the threads should have the same return value.

UPDATE 1

I think I can solve it if I find a way to server the first thread and block every other threads and after completion of first thread's method call, signal the methods result to other threads and also cancel them, to prevent them calling calculator again.

Using lock inside the method also is not what I am looking for, because in that case again the calculator is being called multiple times...

It seems that you need theLazy<T> class. This class provides support for lazy initialization. Here is how you could use it:

Lazy<int> lazyCalculator = new Lazy<int>(calculator);

Task[] tasks = new Task[taskMax];
for (int i = 0; i < taskMax; i++)
{
    tasks[i] = Task.Run(() => _ = lazyCalculator.Value);
}
Task.WaitAll(tasks);

When a Lazy instance is constructed, it can take an optional LazyThreadSafetyMode argument. The default value of this argument is ExecutionAndPublication , with the behavior described below:

Locks are used to ensure that only a single thread can initialize a Lazy<T> instance in a thread-safe manner.

It looks like you want is that your Calculator method can be executed by any thread, but this method should be executed only once . If it is true, then we would use lock statement.

The purpose of lock statement is :

The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock

An example:

static object lockCalculatorMethod = new object();
static int executionCounter = 0;
static int loopMax = 10;
static int taskMax = 10;


static void Main(string[] args)
{
    Task[] tasks = new Task[taskMax];
    for (int i = 0; i < taskMax; i++)
    {
        tasks[i] = Task.Run(() => _ = Calculator());
    }
    Task.WhenAll(tasks);
}

and Calculator method:

static int Calculator()
{
    lock (lockCalculatorMethod)
    {
        if (executionCounter < 1)
        {
            executionCounter++;
            int result = 0;
            for (int i = 0; i < loopMax; i++)
            {
                Thread.Sleep(100);
                if (result + i >= int.MaxValue)
                {
                    result = 0;
                    result += i;
                }
            }
            return result;
         }
         else
             return -1;
     }
}

UPDATE:

If you want to cache result and avoid recalculation while calling by other threads, then you can use threadSafe collection ConcurrentQueue<T> and just get items from this collection:

static object lockCalculatorMethod = new object();
static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();

static int executionCounter = 0;
static int loopMax = 7;
static int taskMax = 7;


static void Main(string[] args)
{
    Task[] tasks = new Task[taskMax];
    for (int i = 0; i < taskMax; i++)
    {
        tasks[i] = Task.Run(() => 
        {
            var result = Calculator();
            Console.WriteLine(result);
        });
    }
    Task.WaitAll(tasks);
}

And Calculator method:

static int Calculator()
{
    int result = 0;
    lock (lockCalculatorMethod)
    {
        int lockResult = 0;
        if (executionCounter < 1)
        {
            executionCounter++;
            for (int i = 0; i < loopMax; i++)
            {
                Thread.Sleep(100);                        
                lockResult += i;
            }
            queue.Enqueue(lockResult);
        }
    }
    queue.TryPeek(out result);
    return result;
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM