1

I am creating console app to simulate server. I create multiple virus files together using multiple threads to see whether all files get quarantined, if yes, how long it takes to quarantined. The problem with multithreading application is one thread starts writing another thread so I get exception - The process can not access the file X because the file is being used by another process. This is the reason that all files don't get quarantined. I use framework 4.5.2 I have created app using thread and task. I don't get the desire result. What is the best practice to write this app? Thank you for helping me in advance.

Using Thread:

class Program
{
    static string folderPath;
    static readonly string fileContent = @"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";

    static void Main(string[] args)
    {
        folderPath = "F:\VirusScan";

        int counter = 1000;
        for (int i = 0; i < counter; i++)
        {
            var thread = new Thread(() => GenerateVirusFile(i));
            thread.Start();
        }

        Console.ReadKey();
    }

    static void GenerateVirusFile(int i)
    {
        string filePath = $@"{folderPath}\TestForVirusScan_{i}_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.txt";

        try
        {
            using (StreamWriter writer = new StreamWriter(filePath))
            {
                writer.WriteLine(fileContent);
            }

            var timer = Stopwatch.StartNew();
            while (true)
            {
                if (!File.Exists(filePath))
                {
                    Console.WriteLine($"{i}: File was removed in {timer.ElapsedMilliseconds}ms");
                    break;
                }
                else
                {
                    Thread.Sleep(1);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{i}: Exception {ex.GetType().Name} occurred: {ex.Message}");
        }
    }
}

Using Task:

class Program
{
    static string folderPath;
    static readonly string fileContent = @"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";

    static void Main(string[] args)
    {
        folderPath = "F:\VirusScan";

        int counter = 1000;
        List<Task> tasks = new List<Task>();

        for (int i = 1; i <= counter; i++)
        {
            Task newTask = new Task((x) => GenerateVirusFile(x), i);                
            tasks.Add(newTask);
        }

        foreach (var task in tasks)
        {
            task.Start();
        }

        Task.WaitAll(tasks.ToArray()); 

        Console.ReadKey();
    }

    public static void GenerateVirusFile(object i)
    {
        string filePath = $@"{folderPath}\TestForVirusScan_{i}_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.txt";

        try
        {
            using (StreamWriter writer = new StreamWriter(filePath))
            {
                writer.WriteLine(fileContent);
            }

            var timer = Stopwatch.StartNew();
            while (true)
            {
                if (!File.Exists(filePath))
                {
                    Console.WriteLine($"{i}: File was removed in {timer.ElapsedMilliseconds}ms");
                    break;
                }
                else
                {
                    Thread.Sleep(1);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{i}: Exception {ex.GetType().Name} occurred: {ex.Message}");
        }
    }
}
Hiral
  • 153
  • 5
  • 18
  • 1
    the reason you are getting that error is because you are trying to read from the **same file** from **multiple threads**. change your code to read once this does not need to be done in each thread and can not be done at the same time, once pass a copy of the contents to the multiple threads for writing.... ensuring their names are unique if same directory. – Seabizkit Sep 18 '19 at 18:51
  • this is like the strangest things ive seen to test, what would you gain from knowing if it was 1 sec or 1 min, it would depend if you antivirus software has on-access* scanning on or not, if not you would have to scheduled a scan, hence most gd once hence on-access scanning. – Seabizkit Sep 18 '19 at 18:55
  • It isn't the same file, though, unless I'm missing something. The variable `i` is passed to each thread/task and incorporated into the file name. – Eric J. Sep 18 '19 at 18:56
  • Note that trying to start 1000 tasks isn't going to make all 1000 writes happen concurrently. There's a limit to how many tasks (or threads) will be scheduled. – Eric J. Sep 18 '19 at 18:57
  • 1
    You can cut down significantly on IO requests (and improve the concurrency of your test) by using a file watcher to watch for deletion rather than polling File.Exists in a tight loop for a thousand files. – Eric J. Sep 18 '19 at 18:57
  • @Seabizkit I am not reading file, I am writing in file. I want to see how fast files get quarantined so that I can put that much wait in real application. – Hiral Sep 18 '19 at 19:01
  • @EricJ.Even tho I take 200 files, I still get exception. – Hiral Sep 18 '19 at 19:01
  • https://stackoverflow.com/questions/26741191/ioexception-the-process-cannot-access-the-file-file-path-because-it-is-being, maybe its the anti virus software "being used by another process" – Seabizkit Sep 18 '19 at 19:15
  • @Hiral please check my solution below, it should work out – Nicklaus Brain Sep 19 '19 at 09:11

2 Answers2

1

The problem is in the following code:

for (int i = 0; i < counter; i++)
{
    var thread = new Thread(() => GenerateVirusFile(i));
    thread.Start();
}

The closure () => GenerateVirusFile(i) is referencing changing variable Rewrite it in the following way:

Parallel.For(0, counter, GenerateVirusFile);
Nicklaus Brain
  • 884
  • 6
  • 15
0

Have you tried something like this in your loop:

    int x = i;
    var thread = new Thread(() => GenerateVirusFile(x));

this prevents that the same i is used for more threads/file names.