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]