Ruby and Tk question

S

Stephen Kellett

Hello everyone,

I've been trying to write a trivial multi-threaded application that has
a GUI (implemented using TkRuby) and which updates various counters on
the GUI. Problem is it deadlocks as soon as I try to update the
graphical part, but I am unsure why. I've included the most stripped
down version of the code here - which will not deadlock - it will just
output to stdout. If you comment in the two obvious lines (commented to
indicate which ones) then it does deadlock.

Am I doing something wrong?
Have I made incorrect assumptions?
How should I do this?
Is it possible to update Tk components from threads other than the main
GUI thread?

Cheers

Stephen

require 'tk' # get widget classes
require 'thread'

class RTVExample < TkRoot
def initialize(args)
super(args)

@countA = 0
@countB = 0
@lock1 = Mutex.new

# create our UI

@menubar = TkMenubar.new(nil, nil)
@menubar.pack('side'=>'top', 'fill'=>'x')

@menubar.add_menu([['File', 0],
['Exit', proc{exitFunc}, 0]])

@menubar.add_menu([['Thread Test', 0],
['Test 1', proc{testThread1}, 0]])

@counterLabel = TkLabel.new{text 'Counter' ; pack {padx=30 ; pady=0}}
end

# why does this deadlock? - surely it should just run the two threads alongside the GUI thread.

def testThread1()
# create two the threads - comment in the #@counter lines to see the deadlock

tok1 = Thread.new do
10000.times {
@lock1.synchronize { @countA += 1 }
puts "A:" + @countA.to_s()

# this next line causes the deadlock - why?
#@counterLabel.configure(text=>"Counter A: %d" % @countA)
}
end
tok2 = Thread.new do
10000.times {
@lock1.synchronize { @countB += 1 }
puts "B:" + @countB.to_s()

# this next line causes the deadlock - why?
#@counterLabel.configure(text=>"Counter B: %d" % @countB)
}
end

tok1.join
tok2.join
end

def exitFunc()
TkRoot.destroy()
end
end

# run the GUI app

gui = RTVExample.new()
Tk.mainloop()
 
D

Daniel Lewis

Deadlocks are usually to do with the OS. Maybe Tk can't cope with
threads. Have you tried this in other Graphical Toolkits, WxRuby is
quite nice and GTK seems quite good.
 
S

Stephen Kellett

Daniel Lewis said:
Deadlocks are usually to do with the OS. Maybe Tk can't cope with
threads. Have you tried this in other Graphical Toolkits, WxRuby is
quite nice and GTK seems quite good.

I haven't tried other GUI toolkits (I wasn't particularly aware of them
- the PickAxe book talks about Tk). The equivalent code with Python and
Tk runs just fine.

Stephen
 
H

Hidetoshi NAGAI

From: Stephen Kellett <[email protected]>
Subject: Ruby and Tk question
Date: Thu, 29 Sep 2005 19:41:45 +0900
Message-ID: said:
I've been trying to write a trivial multi-threaded application that has
a GUI (implemented using TkRuby) and which updates various counters on
the GUI. Problem is it deadlocks as soon as I try to update the
graphical part, but I am unsure why. I've included the most stripped
down version of the code here - which will not deadlock - it will just
output to stdout. If you comment in the two obvious lines (commented to
indicate which ones) then it does deadlock.

Am I doing something wrong?
Have I made incorrect assumptions?
How should I do this?
Is it possible to update Tk components from threads other than the main
GUI thread?

On Ruby/Tk, all operations and callbacks are evaluated on the
eventloop thread. All calls are serialized to avoid conflict of
GUI operation. Therefore, never wait or sleep on the callback
operation!!

Your callback "testThread1" waits two threads.
That is not so serious problem except broken respense of GUI.
However, each of the threads which "testThread1" waits calls
a Tk operation "@counterLabel.configure".
The Tk operation sends a request to the eventloop and waits
for the return value from the eventloop.
It makes deadlock!.

If "testThread1" doen't the created threads, there is no problem
except your typo ;-)
#@counterLabel.configure(text=>"Counter A: %d" % @countA) ^^^^
#@counterLabel.configure(text=>"Counter B: %d" % @countB)
^^^^
:text or "text"

However, I think, your request is "disable the menu entry between
callback operations are working".
If that is true, Please read the rewrited example at the end of
this mail.

# BTW, TkTimer class may help you sometimes. For example,
# -------------------------------------------------------------
# TkTimer.new(0, 1000){ <operations of each call> }.start
# -------------------------------------------------------------
# This calls the block 1000 times with 0ms (or minimum) interval.
# TkTimer works on the eventloop. So, there is no thread-switching.

---------< example script >--------------------------------------
require 'tk' # get widget classes
require 'thread'

class RTVExample < TkRoot
def initialize(args)
super(args)

@countA = 0
@countB = 0
@lock1 = Mutex.new

# create our UI

@menubar = TkMenubar.new(nil, nil)
@menubar.pack('side'=>'top', 'fill'=>'x')

@menubar.add_menu([['File', 0],
['Exit', proc{exitFunc}, 0]])

@th_test_mbtn, @th_test_menu =
@menubar.add_menu([['Thread Test', 0],
['Test 1', proc{testThread1}, 0]])

@counterLabel = TkLabel.new{text 'Counter' ; pack {padx=30 ; pady=0}}
end

# why does this deadlock? - surely it should just run the two threads alongside the GUI thread.

def testThread1()
# create two the threads - comment in the #@counter lines to see the deadlock
Thread.new do
begin
@th_test_menu.entryconfigure('Test 1', :state=>:disabled)

tok1 = Thread.new do
10000.times {
@lock1.synchronize { @countA += 1 }
puts "A:" + @countA.to_s()

# this next line causes the deadlock - why?
@counterLabel.configure:)text=>"Counter A: %d" % @countA)
Tk.update_idletasks
}
end
tok2 = Thread.new do
10000.times {
@lock1.synchronize { @countB += 1 }
puts "B:" + @countB.to_s()

# this next line causes the deadlock - why?
@counterLabel.configure:)text=>"Counter B: %d" % @countB)
Tk.update_idletasks
}
end

begin
tok1.join
rescue
# ignore error on Thread
end
begin
tok2.join
rescue
# ignore error on Thread
end
ensure
@th_test_menu.entryconfigure('Test 1', :state=>:normal)
end
end
end

def exitFunc()
TkRoot.destroy()
end
end

# run the GUI app

gui = RTVExample.new()
Tk.mainloop()
-----------------------------------------------------------------
--
Hidetoshi NAGAI ([email protected])

Cheers

Stephen

require 'tk' # get widget classes
require 'thread'

class RTVExample < TkRoot
def initialize(args)
super(args)

@countA = 0
@countB = 0
@lock1 = Mutex.new

# create our UI

@menubar = TkMenubar.new(nil, nil)
@menubar.pack('side'=>'top', 'fill'=>'x')

@menubar.add_menu([['File', 0],
['Exit', proc{exitFunc}, 0]])

@menubar.add_menu([['Thread Test', 0],
['Test 1', proc{testThread1}, 0]])

@counterLabel = TkLabel.new{text 'Counter' ; pack {padx=30 ; pady=0}}
end

# why does this deadlock? - surely it should just run the two threads alongside the GUI thread.

def testThread1()
# create two the threads - comment in the #@counter lines to see the deadlock

tok1 = Thread.new do
10000.times {
@lock1.synchronize { @countA += 1 }
puts "A:" + @countA.to_s()

# this next line causes the deadlock - why?
#@counterLabel.configure(text=>"Counter A: %d" % @countA)
}
end
tok2 = Thread.new do
10000.times {
@lock1.synchronize { @countB += 1 }
puts "B:" + @countB.to_s()

# this next line causes the deadlock - why?
#@counterLabel.configure(text=>"Counter B: %d" % @countB)
}
end

tok1.join
tok2.join
end

def exitFunc()
TkRoot.destroy()
end
end

# run the GUI app

gui = RTVExample.new()
Tk.mainloop()

--
Stephen Kellett
Object Media Limited http://www.objmedia.demon.co.uk/software.html
Computer Consultancy, Software Development
Windows C++, Java, Assembler, Performance Analysis, Troubleshooting
 
S

Stephen Kellett

In message <[email protected]>, Hidetoshi

Thank you for answer. Most helpful.

Stephen
On Ruby/Tk, all operations and callbacks are evaluated on the
eventloop thread. All calls are serialized to avoid conflict of
GUI operation. Therefore, never wait or sleep on the callback
operation!!

....
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top