2

I have code like this, some threads will generate string (here 100) and store to array, other single thread will read data using indexer [] and save to file. I used Interlocked class for string swapping in ThreadSafeString class but I don't know if its enough to avoid deadlocks and non thread safe problems. Other idea I have is to use ConcurrentDirectory<int, string> to be sure its safe.

using System;
using System.IO;
using System.Threading;

namespace Test
{
    class ThreadSafeString
    {
        private string _string;

        public string Value
        {
            get => Interlocked.CompareExchange(ref _string, null, null);
            set => Interlocked.Exchange(ref _string, value);
        }
    }

    class Program
    {
        private static readonly ThreadSafeString[] array = new ThreadSafeString[100];

        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++) array[i] = new ThreadSafeString();

            for (int i = 0; i < 100; i++)
            {
                var thread = new Thread(() =>
                {
                    while (true)
                    {
                        string data = "";
                        //generate data

                        array[i].Value = data;
                        Thread.Sleep(100);
                    }
                });
                thread.Name = i.ToString();
                thread.Start();
            }

            var ht = new Thread(() =>
            {
                while (true)
                {
                    for (int i = 0; i < 100; i++)
                    {
                        string temp = array[i].Value;
                        File.WriteAllText(path, temp);
                    }

                    Thread.Sleep(2000);
                }
            });
            ht.Start();

            Console.ReadKey();
        }
    }
}
4
  • 3
    why are you doing this is this an exercise for school? otherwise use the Concurrent Collections built into .Net Commented Oct 15, 2019 at 13:57
  • Sounds like a job for... anything else. Parallel LINQ (Enumerable.AsParallel()), the TPL, Parallel.For[Each], Tasks, take your pick. Manually gathering results from parallel computations is something you ideally should not want to bother with yourself. Commented Oct 15, 2019 at 13:57
  • @3v0 thread-safe list Commented Oct 15, 2019 at 14:12
  • Can you drop the thread writing to the file? Or have one file per thread? Seems like having the process being executed on the thread output it's own status into it's own file would be a much simpler solution... Commented Oct 15, 2019 at 14:39

2 Answers 2

1

I don't see a point in ThreadSafeString here. Use Interlocked.CompareExchange if you need to compare, store and return old data as a single operation.

Interlocked.CompareExchange(ref _string, null, null);

means

if (_string == null)
{
    _string = null;
    return null;
} else return _string;

but as a single operation. As you can see it can be easily replaced with

return __string;

same with Interlocked.Exchange - you don't need to do it for strings:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/variables#atomicity-of-variable-references

Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types.

You need to do it only for types other than that and if you need to get old value as an atomic operation. Simply using string[] here will have same thread-safety. You can safely store and read data from array as long as you don't change array size and operate on reference types or single word sized value types. You only need to worry about thread-safety if you want to read, change and store data back, but this is another problem that is not even solved by collections like ConcurrentDirectory

Sign up to request clarification or add additional context in comments.

1 Comment

sure it is. This is how all of these thread-safe collections work underneath. The only thing they do is they version arrays and regulates access to them
0

EDIT: To reflect your comment I changed to the use of ConcurrentDictionary.

The dictionary will take care for the threadsafe reading and writing of the strings.

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace TestConsole
{
    internal class Program
    {
        private static void Main()
        {
            ConcurrentDictionary<int, string> threadResults = new ConcurrentDictionary<int, string>();

            for (int i = 0; i < 100; i++)
            {
                new Thread(() =>
                {
                    while (true)
                    {
                        string data = i.ToString();
                        threadResults.AddOrUpdate(i, data, (id, x) => data);
                        Thread.Sleep(100);
                    }
                }).Start();
            }

            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    foreach (var threadResult in threadResults)
                    {
                        Console.WriteLine($"{threadResult.Key} {threadResult.Value}");
                    }

                    Console.WriteLine();
                    Thread.Sleep(2000);
                }
            });

            Console.WriteLine("press return to exit...");
            Console.ReadLine();
        }
    }
}

1 Comment

@3v0 i updated my answer to reflect your comment. Hope I got you right.

Your Answer

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