Re: yet another thread question



Hi John,

Thanks for taking the time to provide such a great answer. I REALLY appreciate it.

I need to investigate semaphores more, but in the mean time, I'd like to try the first solution you gave 
(hopefully tonight or tomorrow). I have a question though.

If I understand the first solution correctly, we're creating a separate idle function for each message. If 
the worker thread gets a bit ahead of the GUI updates, then a few idle functions might pile up. ok. But one 
thing I don't understand is that these idle functions will be spawned by the worker thread (not from the main 
gtk context), and I thought only the main gtk iteration was allowed to muck around with the textview (or any 
other part of the GUI).

Originally I had the worker function attempting to update the textview, but it seg-faulted. Making an idle 
function spawned by the main gtk iteration do it solved the seg-faulting.

Can I really have idle functions spawned from the worker thread update the textview?

BTW, my program shouldn't fill memory because there's really only maybe a total of 40 to 50 lines of text - 1 
line per message - to be put into the textview.


Dave



________________________________
 From: "jcupitt gmail com" <jcupitt gmail com>
To: David Buchan <pdbuchan yahoo com> 
Cc: gtk-app-devel-list list <gtk-app-devel-list gnome org> 
Sent: Friday, July 6, 2012 3:56 AM
Subject: Re: yet another thread question
 
Hi again David,

On Friday, 6 July 2012, David Buchan wrote:

When the user presses a button, an idle function is begun to watch a flag which will tell it a message has 
been left in a string for it by a worker thread. The worker thread is then started. It will produce results 
to be displayed in a textview by the idle function. When the worker thread has results, it puts them into 
the string and sets the message_available_flag. The idle function sees the flag set, prints the results to 
the textview, and then clears the message_available_flag.

There's a much simpler solution -- just malloc the string in the
worker and pass it to the main thread as an idle argument. Something
like:

worker()
{
  char *str;

  for(;;) {
    str = g_strdup("hello world!\n");
    g_idle_add(from_worker_cb, str);
    sleep(1);
  }
}

gboolean
from_worker(char *str)
{
  update_textview(str);
  g_free(str);
  return FALSE;
}

Now there's effectively a flexible buffer between the worker and the
main thread, so one can briefly run ahead of the other if it has to.
As long as update_textview() typically takes less time than an
iteration of your worker's loop, you'll be fine.

If the worker is always very quick and textview is always very slow
you could fill memory. But this is very unlikely: updating your
program's model should be quick (or your program is badly designed)
and an iteration of a worker should be slow (otherwise, why bother
making a worker).

You could make the buffer bounded with a semaphore. Something like:

// start it at count 10
Semaphore *buffer_length = semaphore_new(10);

worker()
{
  char *str;

  for(;;) {
    // decrement the semaphore
    // if the semaphore is zero, block until it's >0
    // then try again
    semaphore_down(buffer_length);
    str = g_strdup("hello world!\n");
    g_idle_add(from_worker_cb, str);
  }
}

gboolean
from_worker(char *str)
{
  update_textview(str);
  g_free(str);
  // increment the semaphore
  // this will never block us, but can unblock anything
  // waiting on this semaphore
  semaphore_up(buffer_length);
  return FALSE;
}

Using locks and shared variables to synchronise threads can be very
hard, as you found. Races and deadlocks will bite you badly,
especially in more complicated situations.

I find semaphores a nice abstraction: they encapsulate the slightly
tricky process of read / lock / signal in a simple way. They are a bit
old-fashioned now though, which is why I guess glib does not have a
direct implementation.

John


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]