3

I have a WPF script running into problems recently.

The script does subnet scanning using PSParallel module. When the subnet CIDR is less than 21, it starts having the problem.

The problem is: I have a concurrent queue as a member of a synchronized hash table. The queue is used for producer/consumer model. The scanning threads keep enqueue the data and the GUI thread keep dequeue data and write it to a richtextbox. The dequeue process is handled by a dispatch timer event handler, which is executed every 20 ms. When the CIDR >=21, there is no problem. But when CIDR <21, sometimes it will throw an error message:

Collection was modified; enumeration operation may not execute.
At E:\PSScanner\PSScanner.ps1:446 char:8
+     if($syncHash.Q.Count -ne 0){
+        ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException

I think the synchronized hash table is thread safe and the concurrent queue is thread safe as well. Not sure why is this happening.

The source code is here: https://github.com/MeCRO-DEV/PSScanner

Digger
  • 55
  • 6
  • Please create an [mcve] and add that (as code) to the question. – iRon Apr 12 '21 at 06:03
  • I guess a thread safe *queue* is something really specific, see: [C++11 thread-safe queue](https://stackoverflow.com/q/15278343) – iRon Apr 12 '21 at 07:25
  • Thanks, iRon. I just read the document again @ https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1.isempty?view=net-5.0 – Digger Apr 12 '21 at 18:15
  • For determining whether the collection contains any items, use of this property is recommended rather than retrieving the number of items from the Count property and comparing it to 0. However, as this collection is intended to be accessed concurrently, it may be the case that another thread will modify the collection after IsEmpty returns, thus invalidating the result.. – Digger Apr 12 '21 at 18:15
  • I have changed the if statement to if(!($syncHash.Q.IsEmpty)){}. Hopefully I don't get that error again. – Digger Apr 12 '21 at 18:16
  • It's hard to make reprex at this moment as I need to get rid of all GUI code. The problem is very rare and hard to catch. – Digger Apr 12 '21 at 18:19

1 Answers1

1

I fixed the problem eventually.

I was calling a scriptblock stored in a synchronized hash table: $syncHash.Output, which send the output string to a concurrent queue. That scriptblock causes the output messed up and the worker threads terminated unexpectedly. After enqueue the data directly, instead of calling the scriptblock, problem solved.

Also, I have an IP counter $syncHash.Count. I thought it should be thread-safe as it is stored in the synchronized hash table, but actually it is not. I was getting inaccurate counts every time. After setting up a mutex to protect the counter variable, it works perfectly. The only reason I was thinking is the counter is updated in worker threads which are PSParallel instances(Invoke-Parallel cmdlet). Not sure how it handles the hash table.

Thank you all for your help.

Digger
  • 55
  • 6