5

After a lot of research, and after reading and trying all of the questions on here, I think it's time for me to ask for some help.

I have an application in C#, and I'm trying to write in the SAME file with different thread.

public static void LaunchThreads(string path_file)
{
    int i = 0;
    Dictionary<int, Thread> threadsdico = new Dictionary<int, Thread>();
    while (i < MAX_THREAD)
    {
            Thread thread = new Thread(() => ThreadEntryWriter(string path_file));
            thread.Name = string.Format("ThreadsWriters{0}", i);
            threadsdico.Add(i, thread);
            thread.Start();
            i++;
    }
    int zz = 0;
    while (zz < threadsdico.Count())
    {
        threadsdico[zz].Join();
        zz++;
    }
}
private static readonly Object obj = new Object();
public static void ThreadEntryWriter(string path_file)
{
    int w = 0;
    while (w < 99)
    {       
        string newline = w + " - test" + "\r";
        lock(obj)
        {
            string txt = File.ReadAllText(path_file);
            using (TextWriter myWriter = new StreamWriter(path_file))
            {   
                TextWriter.Synchronized(myWriter).Write(txt + newline);
            }
        }
        w++;
    }
}

I've try everything, my code is globally like that, but I've try every way, with every lock, every file open method, but I keep getting The process cannot access the files because it's in use. The line who generate this error is this one using (TextWriter myWriter = new StreamWriter(path_file)).

I tried a lot of thing, closing the files etc, but when threads start to work at the same time, the program stop and give me the error The process cannot access the files because it's in use (self-explain). But I don't understand why, the lock is suppose to block another thread to enter here. And I used Synchronized method to write which is thread safe. Well sorry for the long writing it's my first post here.

VMAtm
  • 27,943
  • 17
  • 79
  • 125
tucotraff
  • 61
  • 4
  • "the program stop and give me the error." - can you share your error? – Anton Komyshan Feb 02 '17 at 18:08
  • 2
    Are you sure there is no other code that opens the file? If yes - turn off antivirus scanners and try again. – Alexei Levenkov Feb 02 '17 at 18:08
  • the error is something like "The process cannot access the files because it's in use". – tucotraff Feb 02 '17 at 18:10
  • And yes I'm sure nothing else open the files, because it works perfectly fine when it's not in threading, but thanks for the Antivir thing I will try it – tucotraff Feb 02 '17 at 18:11
  • What if you use `File.WriteAllText` instead of stream writer, do you get the same error? – Ivan Leonenko Feb 02 '17 at 18:18
  • 4
    `TextWriter.Synchronized` returns a new instance (a wrapper for your original TextWriter). Maybe you need to `Dispose` that instance too? – John Wu Feb 02 '17 at 18:19
  • Agree with John Wu, also `TextWriter.Synchronized` isn't need because you are already using a lock on a static object to ensure only one process can open and write to the file at a time. You could even save yourself some code and just use `File.AppendAllText(file_path, txt + newline);` inside your locked block. – RonC Feb 02 '17 at 18:37
  • @JohnWu `TextWriter.Synchronized` indeed returns new writer that *should* be disposed (and not needed in this case at all), but it can't block the file as all it doing is redirecting calls to original writer under lock and code already disposing original writer. So unlikely the reason for the behavior OP sees. – Alexei Levenkov Feb 02 '17 at 19:01
  • @John Thanks for your comment but as Alexei said, I just tried it and it didn't work. Ron Thank for your help. It's true that I will normally don't have to use Synchronize because of my lock, but as explained, my lock did not work, because if it were, there will not be this problem, that's why I tried to add Synchronize and other stuff, but without success. And I'm sure the lock doesn't have any effect because if I run just 2 thread, espaced with 20 second, there is no problem and they can both write in the file, but if they start at the same time, there is a conflict IN the LOCK block. – tucotraff Feb 02 '17 at 23:11
  • And yes Ivan, I get the same error. I really think the problem is when my code read the file, but another thread is at the same time writing in it, which is normally impossible because of my lock(obj). @AlexeiLevenkov I tried to desactivate my antivir but no success, the confusion don't come from here, I really think my lock code isn't correctly locked the thread – tucotraff Feb 02 '17 at 23:13
  • I also have this code in my LaunchThreads function, to Join them all and wait for them to finish. Maybe this Join can cause a confusion with my lock? (see the edit of LaunchThreads function) – tucotraff Feb 02 '17 at 23:15
  • Suggest to print logs before after accessing file, and run it to see if anything weird happens when you get the file cannot be accessed error message... – jiulongw Feb 02 '17 at 23:15
  • Any chance the file is in use by something other than your program? – John Wu Feb 02 '17 at 23:31
  • [This](http://stackoverflow.com/questions/6350224/does-filestream-dispose-close-the-file-immediately) may provide some insight. – John Wu Feb 02 '17 at 23:43
  • 2
    Was it local file or network shared file? By the way, I tried to repro the issue but my program runs fine with MAX_THREAD=10. (Simply added main function wrapper) – jiulongw Feb 02 '17 at 23:47
  • Seems to be the failure is inside the original code and not in the posted code. How can anyone answer the question without seeing the codepart with the failure? crystal balls are rare ... – Sir Rufo Feb 02 '17 at 23:54
  • There is no issue - I've tried it with MAX_THREAD=50 and it works fine. – tcwicks Feb 03 '17 at 00:22
  • Can you please provide a version of your code that we can run that demonstrates your issue? – Enigmativity Feb 03 '17 at 00:52

3 Answers3

0

Synchronized writer still should be disposed:

var newline = w + " - test";
using (var sw = new StreamWriter(path_file))
using (var sync = TextWriter.Synchronized(sw))
{
    // no need to add a new line char, just use other method to write
    sync.WriteLine(txt + newline);
}

Also, you can save a sync variable and call it's methods from all the threads, and it will do all the work for you, and you can dispose it later after all the text is written.

VMAtm
  • 27,943
  • 17
  • 79
  • 125
0

I test your code and applied some changes and it's working fine without error.

I used winform application to run your code.Please check below code.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication14
{
    public partial class Form1 : Form
    {
        private readonly Object obj = new Object();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void button1_Click(object sender, EventArgs e)
        {
            LaunchThreads("D:\\log.txt");
        }

        public void LaunchThreads(string path_file)
        {
            int i = 0;
            int MAX_THREAD = 10;
            Dictionary<int, Thread> threadsdico = new Dictionary<int, Thread>();
            while (i < MAX_THREAD)
            {
                Thread thread = new Thread(() => ThreadEntryWriter(path_file, string.Format("ThreadsWriters{0}", i)));
                    thread.Name = string.Format("ThreadsWriters{0}", i);
                    threadsdico.Add(i, thread);
                    thread.Start();
                    i++;
            }
            int zz = 0;
            while (zz < threadsdico.Count())
            {
                threadsdico[zz].Join();
                zz++;
            }
        }

        public void ThreadEntryWriter(string path_file,string threadName)
        {
            int w = 0;
            while (w < 99)
            {
                string newline = w + " - test" + " Thread:" + threadName + "\r\n";
                lock (obj)
                {
                    string txt = File.ReadAllText(path_file);
                    using (TextWriter myWriter = new StreamWriter(path_file))
                    {
                        TextWriter.Synchronized(myWriter).Write(txt + newline);
                    }
                }
                w++;
            }
        }
    }
}

I got output as below.

  • 0 - test Thread:ThreadsWriters1
  • 0 - test Thread:ThreadsWriters2
  • 1 - test Thread:ThreadsWriters2
  • 2 - test Thread:ThreadsWriters2
  • 1 - test Thread:ThreadsWriters1
  • ....
  • ....
Hetvi
  • 132
  • 1
  • 1
  • 8
0

It seems like you want to write lines to the same file from multiple threads.

It's quite inefficient to read in all lines every time you want to output a line and then append a line and write it out.

I think the cause of your error is that you aren't disposing the Synchronized TextWriter, so it may well be keeping a reference to the file open even though you dispose the underlying TextWriter.

It's also redundant to use your own locking and the Synchronized TextWriter.

I have changed your code a bit to share a single Synchronized TextWriter and just append lines rather than reading the whole file, appending and then writing it out:

    public static void LaunchThreads(string path_file)
    {
        int i = 0;
        using(var sw = File.CreateText(path_file))
        using(var syncWriter = TextWriter.Synchronized(sw)) 
        {
            Dictionary<int, Thread> threadsdico = new Dictionary<int, Thread>();
            while (i < MAX_THREAD)
            {
                Thread thread = new Thread(() => ThreadEntryWriter(syncWriter));
                thread.Name = string.Format("ThreadsWriters{0}", i);
                threadsdico.Add(i, thread);
                thread.Start();
                i++;
            }
            int zz = 0;
            while (zz < threadsdico.Count())
            {
                threadsdico[zz].Join();
                zz++;
            }
        }
    }

    public static void ThreadEntryWriter(TextWriter writer)
    {
        int w = 0;
        while (w < 99)
        {       
            writer.WriteLine($"{w} - test");
            w++;
        }
    }
Martin Ernst
  • 5,629
  • 2
  • 17
  • 14