Popup while multithreading (rhinocommon): "This action cannot be completed because the other program is busy."


#1

I’m getting the below popup while trying to multithread in a rhinocommon plugin. Any thoughts on what might be causing it and how to correct it?

Thanks,
Sam

“This action cannot be completed because the other program is busy. Choose ‘Switch To’ to activate the busy program and correct the problem.”


(Nathan 'jesterKing' Letwory) #2

This generally happens when you try to access UI elements directly from any of your threads. That is something you cannot do.

/Nathan


#3

I’m finding that simply running simultaneous endless loops turns up the error. Below is an example. Any thoughts on what’s going on and how to avoid it?

Thanks,
Sam

Protected Overrides Function RunCommand(ByVal doc As RhinoDoc, ByVal mode As RunMode) As Result
            'create list of threads
            Dim ThreadList As New List(Of System.Threading.Thread)
            For i = 0 To 5
                ThreadList.Add(New System.Threading.Thread(AddressOf Looper))
            Next
            'start threads
            For i = 0 To 5
                ThreadList(i).Start()
            Next
            'join threads
            For i = 0 To 5
                ThreadList(i).Join()
            Next
            Return Result.Success
        End Function
        Sub Looper()
            Do
            Loop
        End Sub

(Nathan 'jesterKing' Letwory) #4

Commands aren’t run in a separate thread, they block until the are done.

You create five threads, then wait for them to complete through the Join() call. This means Rhino will keep blocking until your threads join.

If you don’t Join() you 'll see the threads keep on running. You’ll also find that your command returns right after the threads have been started. That may or may not what you’re after, but you’ll have to be very careful in how you create your code with such a set up. You’ll most definitely open up a big can of worms.

/Nathan


#5

Hi Nathanletwory,

I’m surprised the commands return right after the threads have been started. Shouldn’t the command wait until the code has finished? Perhaps this is what is causing the “other program is busy” error. Is there some way to prevent this from happening and if so how can I change the code?

Thanks,
Sam


(Nathan 'jesterKing' Letwory) #6

Your current code is not returning right away. Because you are joining your threads the command won’t return until your threads are done. You said your threads are always running, i.e. never returning, your command will be waiting forever for your threads.

You need to ensure your threads exit at some point, allowing them to join.

What I tried to suggest in the earlier post is that you don’t call the join on your threads, but that is going to put you in a tricky situation. Ensuring your threads have a good exit condition is probably the best thing to do.


#7

Hi Nathanletwory,

My threads do return eventually. I was only using the infinite loop to demonstrate that, after some period of time, the “busy program” error pops up. I’m thinking it is because my threads are long that I get this error rather than because I’ve missed a spot where I’m accessing UI elements (I’ve gone through my code many times to eliminate this).

You had originally said accessing UI elements would cause the “busy program” error. But you see what I mean that simultaneous threads with endless loops also trigger the error? Perhaps there is some way to suppress the error? Any suggestions?

Thanks,
Sam


(Nathan 'jesterKing' Letwory) #8

The only way to supress the error is to ensure your command doesn’t take too long. You get the dialog because the command runs on the main thread and it notices it is waiting for other threads.

You’ll have to find a way that allows the app message pump to run. I suppose it is possible, but unfortunately I don’t know how. Maybe @piac or @dale could help?


#9

Hi Sam,

Refer to this discussion. RhinoCycles - Server Busy error
I personally have disabled the OLE warnings when interacting with other software via COM as it can be a slow process.

Cheers,

Jon


(Giulio Piacentino) #10

Yes I think that is the issue – the thread.Join() method stops on the UI thread. When that happens over a few seconds, Windows shows that warning window.


#11

Thanks guys, the following line solved it:

Rhino.Runtime.HostUtils.DisplayOleAlerts(False)

So it is working now, though I’m getting inconsistent runtimes: When running three similar simultaneous threads, sometimes they all finish quickly, much quicker than running them without multithreading. However, sometimes the last thread will drag on for a long time. Could this be related to the OleAlerts being suppressed but still slowing things down or can you think of another possible cause?

Thanks,
Sam


(Giulio Piacentino) #12

Welcome to multithreading. Exact timing is much less consistent when multithreading. It’s like when you have 6 people doing one task. Cooperation might be difficult and there might be resource contention.


#13

But if all the threads but one are finished, why would that last one drag out so long? Perhaps there is something getting hung up related to the DisplayOleAlerts.

If I have a computer with multiple CPUs, perhaps I could spread out over multiple CPUs instead of threads? I know I could do this with multiple rhino instances, but that would get clumsy eventually…

Thanks,
Sam


(Giulio Piacentino) #14

How long do you mean for ‘so long’?


#15

For the simplified tests I’m doing, about a couple minutes.

Thanks,
Sam


(Giulio Piacentino) #16

We all did several tests on multithreading and I’ve never seen anything above a few ms when the model was correct. There must be something else going on. I never used thread.Join, though, because performing SendMessage and COM task right in that place was never a good idea for me. Why do you need them there?


#17

Each thread is a simulation and I have to wait until all of them are done until I can summarize the results. If Join is what is causing the problem, perhaps you have another suggest for how to run code only after all threads have finished?

Thanks,
Sam

Update: I’ve tried it without the Join statement (so no summarizing at end) and it still seems to take (sometimes) much longer than single thread. So I don’t think the Join statement is the issue. Any other thoughts?


(Giulio Piacentino) #18

ManualResetEvent, CountDownEvent, Mutex, ThreadFactory.ContinueWhenAll… there are really a lot of things that can accomplish this. http://stackoverflow.com/questions/263116/c-waiting-for-all-threads-to-complete.

Maybe the easiest for this simpler task is to avoid manually messing with threads entirely and use Parallel.ForEach. This allows to leverage the ThreadPool and let the runtime decide when to multithread. Also, creating a new Thread might not always be required. I do not know all requirements.


(Steve Baer) #20

I would recommend using a GetString class with a SetWaitDuration of around half a second. This way the UI can stay alive and the user can cancel. When the wait duration has completed, you can see if your threads are complete and if not run the Get() again in a loop. This is pretty much what we do in V6 with geometry calculations that are running on multiple background threads.


#21

Hi Steve,

I gave that a try and it did seem to be working earlier today, most of the time, but now it’s not. This multi threading business is oddly inconsistent. I think I’m going to have to put this on hold for a bit to catch up on some other things.

I really appreciate the supports, extremely helpful as always!
Thanks,
Sam