proxb / PoshRSJob

Provides an alternative to PSjobs with greater performance and less overhead to run commands in the background, freeing up the console and allowing throttling on the jobs.
MIT License
542 stars 87 forks source link

Cannot not add member to synchronized arraylist #45

Closed dave1000 closed 9 years ago

dave1000 commented 9 years ago

Passing synchronized arraylist as an argument, using [System.Threading.Monitor]::Enter(***.SyncRoot) to lock the object and adding a new member never gets saved to the array, doesn't throw error. You can only modify an object such as PSObject that has already been added to the array. Works fine with a synchronized hash table. If you manually create a runspace not using PoshRSJob the arraylist works fine.

Thanks

proxb commented 9 years ago

When you say 'passing as an argument', are using the -Argument parameter in Start-RSJob?

proxb commented 9 years ago

Looks like the ArrayList is not being passed into the function via -ArgumentList. Will have to determine what is going on and how to fix it. For the time being, I would recommend using the $Using: variable scope to bring in the ArrayList as it does work properly.

$SyncList = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
1|Start-RSJob {($Using:SyncList).Add(1)} -Verbose|Wait-RSJob|Remove-RSJob
$SyncList
proxb commented 9 years ago

Try the latest version (1.5.2.0) as it should fix the issue.

dave1000 commented 9 years ago

Hi Boe

I have tried the latest version which is 1.5.3.0 in the readme. Are you referring to being able to use the $Using in this latest version or that it should resolve the original issue. I have tested it and the arraylist still does not get modified when passed as an argument. I didn't realize you could modify an object using $Using as well as passing it to the runspace so I will give this a try.

Thanks

proxb commented 9 years ago

Yea, I had to fix a bug in the Pester test that got introduced by my troubleshooting of this issue. Can you comment with the code that you are using so I can see if I can duplicate the issue? In my testing with the synchronized arraylist, I was able to pass it in and update it so I want to make sure I am testing it in the way that you are using it.

The $Using: has been around for a little while and it is definitely the preferred approach to this but I would like to see if I can fix the other issue as well.

proxb commented 9 years ago

Yea, I had to fix a bug in the Pester test that got introduced by my troubleshooting of this issue. Can you comment with the code that you are using so I can see if I can duplicate the issue? In my testing with the synchronized arraylist, I was able to pass it in and update it so I want to make sure I am testing it in the way that you are using it.

The $Using: has been around for a little while and it is definitely the preferred approach to this but I would like to see if I can fix the other issue as well.

dave1000 commented 9 years ago

1. This works:

$arrayList = [System.Collections.ArrayList]::Synchronized((New-Object system.Collections.ArrayList));

1 | Start-RSJob -ScriptBlock { Param ( $arrayList )

$arrayList.Add("Test1");
[System.Threading.Monitor]::Exit($arrayList.SyncRoot);

} -ArgumentList $arrayList;

$arrayList

2. This doesn't:

Import-Module PoshRSJob;

$arrayList = [System.Collections.ArrayList]::Synchronized((New-Object system.Collections.ArrayList));

Start-RSJob -ScriptBlock { Param ( $arrayList )

$arrayList.Add("Test1");
[System.Threading.Monitor]::Exit($arrayList.SyncRoot);

} -ArgumentList $arrayList;

$arrayList

3. This doesn't:

$arrayList = [System.Collections.ArrayList]::Synchronized((New-Object system.Collections.ArrayList));

1..10 | Start-RSJob -ScriptBlock { Param ( $arrayList )

$arrayList.Add("Test1");
[System.Threading.Monitor]::Exit($arrayList.SyncRoot);

} -ArgumentList $arrayList;

$arrayList

In 2. the single rs job has the following error:

Exception calling "Enter" with "1" argument(s): "Value cannot be null." At line:6 char:1

You cannot call a method on a null-valued expression. At line:7 char:1

Exception calling "Exit" with "1" argument(s): "Value cannot be null." At line:8 char:1

In 3. any one of the jobs will succeed but the others will produce the same above error.

proxb commented 9 years ago

Thanks. I will look at all of these and test to come up with a possible solution.

proxb commented 9 years ago

I think I have number 2 solved but number 3 is an odd issue. It appears to pass the collection fine on the initial run but the subsequent RSJobs are only recognizing the value as a string which is why you are getting the errors. I'm digging more into this and will see what I can find.

proxb commented 9 years ago

I've finished a fix for this that I tested against your examples, all of which succeeded. I'll try to get this updated later tonight.

proxb commented 9 years ago

Check out the latest update (V1.5.5.0) and let me know if you are seeing the issue happening.

dave1000 commented 9 years ago

Hi Boe

This is all working fine now, thanks for the quick fix.

Dave

kborowinski commented 7 years ago

Passing synchronized hashtable as argument does not work again.

This does not work:

$hash = [hashtable]::Synchronized(@{Number=0})
$sb = {
    param($syncHash)
    [System.Threading.Monitor]::Enter($syncHash.SyncRoot)
    $syncHash.Number += 10
    [System.Threading.Monitor]::Exit($syncHash.SyncRoot)
}
1..10 | Start-RSJob -Name {$_} -ScriptBlock $sb -ArgumentList $hash | Wait-RSJob | Receive-RSJob

Error:

Exception calling "Enter" with "1" argument(s): "Value cannot be null."
The property 'Number' cannot be found on this object. Verify that the property exists and can be set.
Exception calling "Exit" with "1" argument(s): "Value cannot be null."

This works perfectly:

$hash = [hashtable]::Synchronized(@{Number=0})

$sb = {
    ($using:hash).Number += 10
}

1..10 | Start-RSJob -Name {$_} -ScriptBlock $sb | Wait-RSJob | Receive-RSJob

Components: Powershell 5.0 PoshRSJob 1.7.3.7