Re: DataTable weirdness

From: Chris R. Timmons (crtimmons_at_X_NOSPAM_Xcrtimmonsinc.com)
Date: 03/02/05


Date: Wed, 02 Mar 2005 01:40:09 -0800


"Stephan Steiner" <steiner@isuisse.com> wrote in
news:ux6RRUmHFHA.1476@TK2MSFTNGP09.phx.gbl:

> Chris
>
> Thanks for the link to the article (I've seen it on other
> occasions but never read the entire thing).
>
> I'm currently implementing locks into my software, but I'm
> wondering:
>
> If I iterate through all the elements of a table in thread A,
> and at the same time I write to a cell in the database from a
> thread B (the entire write operation is encapsulated in a lock
> statement). The doc says DB reads are thread safe. So, am I
> correct assuming that the lock in thread B will block until the
> iterator is no longer used?

Stephan,

No. The lock on thread B will block other threads from entering
thread B's locked section until that section is finished executing.
Thread B's lock will not prevent other threads from executing if they
are not trying to enter that same locked section.

In other words, thread A's read operations will run concurrently with
thread B's write operations. This happens because thread A is not
trying to enter thread B's locked section.

It sounds like you might want serialized access to a particular data
structure (atomicity):

  http://www.yoda.arachsys.com/csharp/threads/volatility.shtml

To implement atomicity on a collection like an ArrayList, you can
have all read and write operations lock on the same object reference.

Below is a short program that demonstrates "atomicized" multiple read
and write operations on an ArrayList. Both operations lock the
ArrayList to achieve this. To see the non-atomic behavior, simply
uncomment the DoReads() method's lock statement.

using System;
using System.Collections;
using System.Threading;

namespace Example
{
  public class Test
  {
    private ArrayList a = new ArrayList();

    private void DoWrites()
    {
      int counter = 0;

      while (true)
      {
        lock (a)
        {
          if (a.Count >= 15)
            a.Clear();

          Console.WriteLine("===== BEGIN WRITING =====");
          for (int i = 0; i < 5; i++)
          {
            a.Add(++counter);
            Console.WriteLine("Writing: {0}", counter);
            Thread.Sleep(100);
          }
          Console.WriteLine("===== END WRITING =====");
        }
      }
    }

    private void DoReads()
    {
      while (true)
      {
        // Comment out this lock statement to see
        // non-atomic behavior.
        lock (a)
        {
          Console.WriteLine("===== BEGIN READING =====");
          for (int i = 0; i < a.Count; i++)
          {
            Console.WriteLine("Reading: {0}", (int) a[i]);
            Thread.Sleep(100);
          }
          Console.WriteLine("===== END READING =====");
        }
      }
    }

    public void DoReadsAndWrites()
    {
      ThreadStart doWrites = new ThreadStart(DoWrites);
      Thread doWritesThread = new Thread(doWrites);
      doWritesThread.Start();

      // Start the thread.
      Thread.Sleep(0);

      ThreadStart doReads = new ThreadStart(DoReads);
      Thread doReadsThread = new Thread(doReads);
      doReadsThread.Start();

      // Start the thread.
      Thread.Sleep(0);

      // Use Ctrl-C to end the program.
    }

    public static void Main(string[] args)
    {
      new Test().DoReadsAndWrites();
    }
  }
}
 
> Come to think of it I've experienced similar conditions
> previously: while iterating through the rows in a table, I find
> that I need to delete a row. I can delete the row just fine
> inside the iteration but when I try to get the next element via
> the foreach iterator, I get an exception because the collection
> of lines has changed. So I ended up storing which lines I have
> to delete, then after going through all lines, I deleted the
> lines marked for deletion. In this case, what would an attempt
> to delete a record directly within the iteration (using the lock
> statement) yield? Will the code in the lock statement just be
> skipped because I'm currently interating through the table rows?

I don't think a lock would help in that case. The problem arises
within any kind of loop which iterates over a collection of data and
deletes one or more elements from that collection. It has nothing to
do with locking, but with the repositioning of the elements of the
collection when an element is removed.

When an element is removed, the behavior for most ordered collections
is to move the remaining elements "up" one level to fill the gap
created by the removal. For example, if a collection of numbers
consists of:

1
2
3
4
5

and element 3 is removed, the collection will now have a gap between
2 and 4:

1
2

4
5

The underlying collection will typically remove the gap by moving the
4 and 5 elements "up" to fill the gap:

1
2
4
5

This causes problems for any loop that started with the assumption
that the collection held 5 elements. When the loop reaches its fifth
iteration, it will find no element to retrieve, and it will throw an
exception.

The solution is usually to simply reverse the loop so it iterates
over the collection from the end to the beginning. This won't work
with a foreach loop, because those loops always iterate from the
beginning of the collection to the end. But other kinds of looping
constructs, including for and while, can be reversed to iterate over
the collection from the end to the beginning. In these cases,
deleting elements from the collection will not cause problems.

For example, a for loop constructed like this:

  for (int i = 0; i < myDataView.Count; i++)

can be reversed like this:

  for (int i = myDataView.Count - 1; i >= 0; i--)

-- 
Hope this helps.
Chris.
-------------
C.R. Timmons Consulting, Inc.
http://www.crtimmonsinc.com/


Relevant Pages

  • Re: Independent thread fireing events in ATL Service synchronization requirements?
    ... and hence the loop will not execute, ... m_vec itself is not a pointer, so it cannot be NULL for sure. ... >> Did you lock while adding and removing your objects to the list? ... >>> and made my client list a class with synchronization inside ...
    (microsoft.public.vc.atl)
  • Re: Multivalue tail recursion?
    ... Well, iterate just plain looks nicer/lispier than loop, and ... functions operating on series/vectors/streams of data elements are much ... expressions, because they are typically implemented very inefficiently. ...
    (comp.lang.lisp)
  • Re: PLL convergence problem
    ... >>the feedback loop. ... and that point being the desired lock frequency. ... - multiplier type phase detector. ... which is influenced by the delay in the loop filter. ...
    (comp.dsp)
  • Re: PLL convergence problem
    ... >receiver design about 13 years ago. ... >the feedback loop. ... and that point being the desired lock frequency. ... even just a dip in the overal tracking response can create essentially ...
    (comp.dsp)
  • Re: Symbol clashes: how to avoid them. Part 2
    ... It is _much_ better than loop. ... with iterate - it is truly lispy sublanguage and it obeys all lisp ... All this code is not a final package. ... using many different libraries in one project. ...
    (comp.lang.lisp)