Threading question

Discussion in 'ASP .Net' started by John, Sep 28, 2004.

  1. John

    John Guest

    I've got some reasonably complex business logic in my C# code, in a class
    called by a ASP.NET page. This takes around 3-4 seconds to execute. It's not
    dependent on SQL calls or anything like that.

    I know of the problems with the worker process doing two things at once from
    the main thread pool (The old "Sit in a tight loop for 20 seconds and watch
    all your webapps die!" issue). So I've worked around the issue by spawning
    the business logic into a new thread. The main thread Sleep()'s until it's
    finished, and then continues. eg:

    Button_Click:
    1) start new thread
    2) Sleep until it's done
    3) destroy it

    New thread:
    1) Do something heavy for a few seconds
    2) terminate

    I figured that, since the main thread is sleeping waiting for my new thread,
    I don't need to lock() any objects, I can just access then directly.

    It all seems to work fine, and has solved the strange delays my other
    webapp-users were experiencing.

    Does this sound like good practice?

    Thanks,

    John
     
    John, Sep 28, 2004
    #1
    1. Advertising

  2. John

    Scott Allen Guest

    Hi John:

    I'm not familiar with "sit in a tight loop for 20 seconds and watch
    the webapps die" issue. That is an issue for WinForms applications
    which need a main thread to pump messages to keep the GUI responsive,
    but there in no 'main thread' in a web app.

    I think you are making the app more complicated than it needs to be by
    using two threads to process a single request instead of one thread. I
    don't understand why your users would see strange delays without the
    extra thread. Does the extra thread perform any work to produce
    results that are sent to the client? How do you know you are sleeping
    long enough for the worker thread to finish? Are you using EndInvoke
    or a reset event?

    --
    Scott
    http://www.OdeToCode.com/blogs/scott/


    On Tue, 28 Sep 2004 18:59:32 +0100, "John"
    <> wrote:

    >I've got some reasonably complex business logic in my C# code, in a class
    >called by a ASP.NET page. This takes around 3-4 seconds to execute. It's not
    >dependent on SQL calls or anything like that.
    >
    >I know of the problems with the worker process doing two things at once from
    >the main thread pool (The old "Sit in a tight loop for 20 seconds and watch
    >all your webapps die!" issue). So I've worked around the issue by spawning
    >the business logic into a new thread. The main thread Sleep()'s until it's
    >finished, and then continues. eg:
    >
    >Button_Click:
    >1) start new thread
    >2) Sleep until it's done
    >3) destroy it
    >
    >New thread:
    >1) Do something heavy for a few seconds
    >2) terminate
    >
    >I figured that, since the main thread is sleeping waiting for my new thread,
    >I don't need to lock() any objects, I can just access then directly.
    >
    >It all seems to work fine, and has solved the strange delays my other
    >webapp-users were experiencing.
    >
    >Does this sound like good practice?
    >
    >Thanks,
    >
    >John
    >
     
    Scott Allen, Sep 28, 2004
    #2
    1. Advertising

  3. What you've designed is threading without any of the benefits of threading.
    You create a thread, and then sleep the main thread until it is finished.
    This is exactly equivalent to not creating a new thread at all. The purpose
    of threading is to allow 2 different tasks to be executed at the same time.

    --
    HTH,
    Kevin Spencer
    ..Net Developer
    Microsoft MVP
    I get paid good money to
    solve puzzles for a living

    "John" <> wrote in message
    news:O#...
    > I've got some reasonably complex business logic in my C# code, in a class
    > called by a ASP.NET page. This takes around 3-4 seconds to execute. It's

    not
    > dependent on SQL calls or anything like that.
    >
    > I know of the problems with the worker process doing two things at once

    from
    > the main thread pool (The old "Sit in a tight loop for 20 seconds and

    watch
    > all your webapps die!" issue). So I've worked around the issue by spawning
    > the business logic into a new thread. The main thread Sleep()'s until it's
    > finished, and then continues. eg:
    >
    > Button_Click:
    > 1) start new thread
    > 2) Sleep until it's done
    > 3) destroy it
    >
    > New thread:
    > 1) Do something heavy for a few seconds
    > 2) terminate
    >
    > I figured that, since the main thread is sleeping waiting for my new

    thread,
    > I don't need to lock() any objects, I can just access then directly.
    >
    > It all seems to work fine, and has solved the strange delays my other
    > webapp-users were experiencing.
    >
    > Does this sound like good practice?
    >
    > Thanks,
    >
    > John
    >
    >
     
    Kevin Spencer, Sep 28, 2004
    #3
  4. John

    John Guest

    Hi Scott,

    I'm using Windows 2000 and XP, don't know if it's fixed on 2003. .NET 1.1
    sp1.

    Add a button to your webform, and on the server event handler put something
    like:

    DateTime d = DateTime.Now.AddSeconds(20);
    while (DateTime.Now < d) ;


    Run the form, click the button. See your CPU jump to 100%, and try to use
    another webapp in another browser window. Nothing. Dead. Stuffed :) At least
    until 20 seconds have passed.

    But if you run the code I suggested, other apps remain responsive for this
    20 second wait.... strange! even though CPU is at 100%, because (I assume)
    when the other thread of same priority asks for time, it gets 50% of the
    slots the busy-wait thread was getting.

    I know it's been discussed at length in the past. I'm aware sitting in a
    tight loop for 20 seconds is a stupid thing to do ;) but in my real-life app
    I'm number-crunching with datasets. For maybe 2-4 seconds.

    I check the new thread is finished using Thread.Join() with a timeout
    figure. I guess you could also just Sleep(200) or something and poll
    ThreadStatus.

    My concerns are two-fold:

    1) there might be some weird issue with sync locking, even though *I'm* not
    using them in two threads simultaniously, maybe the system does somehow??

    2) I guess the system has a thread-pool for a good reason, whereas I'm just
    creating threads on demand. Maybe I could overload the system??

    Thanks,

    John

    "Scott Allen" <bitmask@[nospam].fred.net> wrote in message
    news:...
    > Hi John:
    >
    > I'm not familiar with "sit in a tight loop for 20 seconds and watch
    > the webapps die" issue. That is an issue for WinForms applications
    > which need a main thread to pump messages to keep the GUI responsive,
    > but there in no 'main thread' in a web app.
    >
    > I think you are making the app more complicated than it needs to be by
    > using two threads to process a single request instead of one thread. I
    > don't understand why your users would see strange delays without the
    > extra thread. Does the extra thread perform any work to produce
    > results that are sent to the client? How do you know you are sleeping
    > long enough for the worker thread to finish? Are you using EndInvoke
    > or a reset event?
    >
    > --
    > Scott
    > http://www.OdeToCode.com/blogs/scott/
    >
    >
    > On Tue, 28 Sep 2004 18:59:32 +0100, "John"
    > <> wrote:
    >
    >>I've got some reasonably complex business logic in my C# code, in a class
    >>called by a ASP.NET page. This takes around 3-4 seconds to execute. It's
    >>not
    >>dependent on SQL calls or anything like that.
    >>
    >>I know of the problems with the worker process doing two things at once
    >>from
    >>the main thread pool (The old "Sit in a tight loop for 20 seconds and
    >>watch
    >>all your webapps die!" issue). So I've worked around the issue by spawning
    >>the business logic into a new thread. The main thread Sleep()'s until it's
    >>finished, and then continues. eg:
    >>
    >>Button_Click:
    >>1) start new thread
    >>2) Sleep until it's done
    >>3) destroy it
    >>
    >>New thread:
    >>1) Do something heavy for a few seconds
    >>2) terminate
    >>
    >>I figured that, since the main thread is sleeping waiting for my new
    >>thread,
    >>I don't need to lock() any objects, I can just access then directly.
    >>
    >>It all seems to work fine, and has solved the strange delays my other
    >>webapp-users were experiencing.
    >>
    >>Does this sound like good practice?
    >>
    >>Thanks,
    >>
    >>John
    >>

    >
     
    John, Sep 28, 2004
    #4
  5. John

    John Guest

    Thanks for the reply Kevin,

    Try writing a button click event handler (ASP.NET) that does:

    private void Button_Click()
    {
    DateTime d = DateTime.Now.AddSeconds(20);
    while (DateTime.Now < d) ;
    }

    Then try something like:

    private void AnotherButton_Click()
    {
    Thread th = new Thread(new ThreadStart(DoSomething));
    th.Start();
    th.Join();
    }

    private void DoSomething()
    {
    DateTime d = DateTime.Now.AddSeconds(20);
    while (DateTime.Now < d) ;
    }

    Try to use other webapps while these are running.... weird!

    It's been discussed at length before (before anyone tells me it's stupid to
    sit in a tight loop for 20 seconds!!!).

    In my real app I'm crunching numbers (entirely in C#, no SQL involved at
    this stage). It only takes a couple of seconds (no chance of browser
    timeout) but there seems to be a noticable performance change for other .NET
    stuff on the server.

    Thanks,

    John



    "Kevin Spencer" <> wrote in message
    news:...
    > What you've designed is threading without any of the benefits of
    > threading.
    > You create a thread, and then sleep the main thread until it is finished.
    > This is exactly equivalent to not creating a new thread at all. The
    > purpose
    > of threading is to allow 2 different tasks to be executed at the same
    > time.
    >
    > --
    > HTH,
    > Kevin Spencer
    > .Net Developer
    > Microsoft MVP
    > I get paid good money to
    > solve puzzles for a living
    >
    > "John" <> wrote in message
    > news:O#...
    >> I've got some reasonably complex business logic in my C# code, in a class
    >> called by a ASP.NET page. This takes around 3-4 seconds to execute. It's

    > not
    >> dependent on SQL calls or anything like that.
    >>
    >> I know of the problems with the worker process doing two things at once

    > from
    >> the main thread pool (The old "Sit in a tight loop for 20 seconds and

    > watch
    >> all your webapps die!" issue). So I've worked around the issue by
    >> spawning
    >> the business logic into a new thread. The main thread Sleep()'s until
    >> it's
    >> finished, and then continues. eg:
    >>
    >> Button_Click:
    >> 1) start new thread
    >> 2) Sleep until it's done
    >> 3) destroy it
    >>
    >> New thread:
    >> 1) Do something heavy for a few seconds
    >> 2) terminate
    >>
    >> I figured that, since the main thread is sleeping waiting for my new

    > thread,
    >> I don't need to lock() any objects, I can just access then directly.
    >>
    >> It all seems to work fine, and has solved the strange delays my other
    >> webapp-users were experiencing.
    >>
    >> Does this sound like good practice?
    >>
    >> Thanks,
    >>
    >> John
    >>
    >>

    >
    >
     
    John, Sep 28, 2004
    #5
  6. Hi John,

    I have used multi-threading in enterprise-level ASP.Net apps with no
    problems whatsoever. However, that wasn't my point. My point was that your
    use of Threading is equivalent to not using Threading at all, except for the
    extra overhead. Your app will respond in almost exactly the same time as if
    you hadn't used Threading at all.

    A program has a main execution thread, which is sequential. It executes
    exactly ONE instruction at a time. When a function is called, execution
    jumps to the function, and continues until the function exits, and the
    execution moves back to the calling function. Your technique does exactly
    that, but in a more complex manner. You spawn a thread, and put the current
    thread to sleep until it is finished processing.

    Think of it this way. You are a thread, sitting at home, going about your
    business, let's say, cleaning house. Now, you need to buy something from the
    store.

    Non-threaded: You go to the store to buy something. When you're done, you
    come home and go back to work.

    Your Threaded: You send your wife to the store to buy something. While she's
    gone, you take a nap. When she gets back, you go back to work.

    Properly Threaded: You send your wife to the store to buy something, while
    you continue working. When she gets back, you continue working.

    Note that the outcome of the first 2 situations is the same. You might just
    as well have gone to the store yourself. You accomplished the same amount of
    work in the time that you would have if you hadn't sent her at all.

    Okay, now imagine that you're not a thread, but a string. While your wife is
    gone to the store, you muss your hair until it is all matted and knotty.
    When your wife gets home, she doesn't recognize you, and cries "I thought
    you were a string!" You reply "I'm a frayed knot." ;-)

    --
    HTH,
    Kevin Spencer
    ..Net Developer
    Microsoft MVP
    I get paid good money to
    solve puzzles for a living

    "John" <> wrote in message
    news:...
    > Thanks for the reply Kevin,
    >
    > Try writing a button click event handler (ASP.NET) that does:
    >
    > private void Button_Click()
    > {
    > DateTime d = DateTime.Now.AddSeconds(20);
    > while (DateTime.Now < d) ;
    > }
    >
    > Then try something like:
    >
    > private void AnotherButton_Click()
    > {
    > Thread th = new Thread(new ThreadStart(DoSomething));
    > th.Start();
    > th.Join();
    > }
    >
    > private void DoSomething()
    > {
    > DateTime d = DateTime.Now.AddSeconds(20);
    > while (DateTime.Now < d) ;
    > }
    >
    > Try to use other webapps while these are running.... weird!
    >
    > It's been discussed at length before (before anyone tells me it's stupid

    to
    > sit in a tight loop for 20 seconds!!!).
    >
    > In my real app I'm crunching numbers (entirely in C#, no SQL involved at
    > this stage). It only takes a couple of seconds (no chance of browser
    > timeout) but there seems to be a noticable performance change for other

    ..NET
    > stuff on the server.
    >
    > Thanks,
    >
    > John
    >
    >
    >
    > "Kevin Spencer" <> wrote in message
    > news:...
    > > What you've designed is threading without any of the benefits of
    > > threading.
    > > You create a thread, and then sleep the main thread until it is

    finished.
    > > This is exactly equivalent to not creating a new thread at all. The
    > > purpose
    > > of threading is to allow 2 different tasks to be executed at the same
    > > time.
    > >
    > > --
    > > HTH,
    > > Kevin Spencer
    > > .Net Developer
    > > Microsoft MVP
    > > I get paid good money to
    > > solve puzzles for a living
    > >
    > > "John" <> wrote in message
    > > news:O#...
    > >> I've got some reasonably complex business logic in my C# code, in a

    class
    > >> called by a ASP.NET page. This takes around 3-4 seconds to execute.

    It's
    > > not
    > >> dependent on SQL calls or anything like that.
    > >>
    > >> I know of the problems with the worker process doing two things at once

    > > from
    > >> the main thread pool (The old "Sit in a tight loop for 20 seconds and

    > > watch
    > >> all your webapps die!" issue). So I've worked around the issue by
    > >> spawning
    > >> the business logic into a new thread. The main thread Sleep()'s until
    > >> it's
    > >> finished, and then continues. eg:
    > >>
    > >> Button_Click:
    > >> 1) start new thread
    > >> 2) Sleep until it's done
    > >> 3) destroy it
    > >>
    > >> New thread:
    > >> 1) Do something heavy for a few seconds
    > >> 2) terminate
    > >>
    > >> I figured that, since the main thread is sleeping waiting for my new

    > > thread,
    > >> I don't need to lock() any objects, I can just access then directly.
    > >>
    > >> It all seems to work fine, and has solved the strange delays my other
    > >> webapp-users were experiencing.
    > >>
    > >> Does this sound like good practice?
    > >>
    > >> Thanks,
    > >>
    > >> John
    > >>
    > >>

    > >
    > >

    >
    >
     
    Kevin Spencer, Sep 28, 2004
    #6
  7. John

    John Guest

    LOL! the old ones are always the best!

    But my example demonstrates why I'm using a new thread - I don't want to do
    two things at once in the same page in this instance, I just want to allow
    other apps (and users on the same app) to run while my page is busy for a
    few seconds.

    My code seems to fix the problem demonstrated in the example. I think ur
    saying it's ok, and safe code! Just checking.

    John


    "Kevin Spencer" <> wrote in message
    news:...
    > Hi John,
    >
    > I have used multi-threading in enterprise-level ASP.Net apps with no
    > problems whatsoever. However, that wasn't my point. My point was that your
    > use of Threading is equivalent to not using Threading at all, except for
    > the
    > extra overhead. Your app will respond in almost exactly the same time as
    > if
    > you hadn't used Threading at all.
    >
    > A program has a main execution thread, which is sequential. It executes
    > exactly ONE instruction at a time. When a function is called, execution
    > jumps to the function, and continues until the function exits, and the
    > execution moves back to the calling function. Your technique does exactly
    > that, but in a more complex manner. You spawn a thread, and put the
    > current
    > thread to sleep until it is finished processing.
    >
    > Think of it this way. You are a thread, sitting at home, going about your
    > business, let's say, cleaning house. Now, you need to buy something from
    > the
    > store.
    >
    > Non-threaded: You go to the store to buy something. When you're done, you
    > come home and go back to work.
    >
    > Your Threaded: You send your wife to the store to buy something. While
    > she's
    > gone, you take a nap. When she gets back, you go back to work.
    >
    > Properly Threaded: You send your wife to the store to buy something, while
    > you continue working. When she gets back, you continue working.
    >
    > Note that the outcome of the first 2 situations is the same. You might
    > just
    > as well have gone to the store yourself. You accomplished the same amount
    > of
    > work in the time that you would have if you hadn't sent her at all.
    >
    > Okay, now imagine that you're not a thread, but a string. While your wife
    > is
    > gone to the store, you muss your hair until it is all matted and knotty.
    > When your wife gets home, she doesn't recognize you, and cries "I thought
    > you were a string!" You reply "I'm a frayed knot." ;-)
    >
    > --
    > HTH,
    > Kevin Spencer
    > .Net Developer
    > Microsoft MVP
    > I get paid good money to
    > solve puzzles for a living
    >
    > "John" <> wrote in message
    > news:...
    >> Thanks for the reply Kevin,
    >>
    >> Try writing a button click event handler (ASP.NET) that does:
    >>
    >> private void Button_Click()
    >> {
    >> DateTime d = DateTime.Now.AddSeconds(20);
    >> while (DateTime.Now < d) ;
    >> }
    >>
    >> Then try something like:
    >>
    >> private void AnotherButton_Click()
    >> {
    >> Thread th = new Thread(new ThreadStart(DoSomething));
    >> th.Start();
    >> th.Join();
    >> }
    >>
    >> private void DoSomething()
    >> {
    >> DateTime d = DateTime.Now.AddSeconds(20);
    >> while (DateTime.Now < d) ;
    >> }
    >>
    >> Try to use other webapps while these are running.... weird!
    >>
    >> It's been discussed at length before (before anyone tells me it's stupid

    > to
    >> sit in a tight loop for 20 seconds!!!).
    >>
    >> In my real app I'm crunching numbers (entirely in C#, no SQL involved at
    >> this stage). It only takes a couple of seconds (no chance of browser
    >> timeout) but there seems to be a noticable performance change for other

    > .NET
    >> stuff on the server.
    >>
    >> Thanks,
    >>
    >> John
    >>
    >>
    >>
    >> "Kevin Spencer" <> wrote in message
    >> news:...
    >> > What you've designed is threading without any of the benefits of
    >> > threading.
    >> > You create a thread, and then sleep the main thread until it is

    > finished.
    >> > This is exactly equivalent to not creating a new thread at all. The
    >> > purpose
    >> > of threading is to allow 2 different tasks to be executed at the same
    >> > time.
    >> >
    >> > --
    >> > HTH,
    >> > Kevin Spencer
    >> > .Net Developer
    >> > Microsoft MVP
    >> > I get paid good money to
    >> > solve puzzles for a living
    >> >
    >> > "John" <> wrote in message
    >> > news:O#...
    >> >> I've got some reasonably complex business logic in my C# code, in a

    > class
    >> >> called by a ASP.NET page. This takes around 3-4 seconds to execute.

    > It's
    >> > not
    >> >> dependent on SQL calls or anything like that.
    >> >>
    >> >> I know of the problems with the worker process doing two things at
    >> >> once
    >> > from
    >> >> the main thread pool (The old "Sit in a tight loop for 20 seconds and
    >> > watch
    >> >> all your webapps die!" issue). So I've worked around the issue by
    >> >> spawning
    >> >> the business logic into a new thread. The main thread Sleep()'s until
    >> >> it's
    >> >> finished, and then continues. eg:
    >> >>
    >> >> Button_Click:
    >> >> 1) start new thread
    >> >> 2) Sleep until it's done
    >> >> 3) destroy it
    >> >>
    >> >> New thread:
    >> >> 1) Do something heavy for a few seconds
    >> >> 2) terminate
    >> >>
    >> >> I figured that, since the main thread is sleeping waiting for my new
    >> > thread,
    >> >> I don't need to lock() any objects, I can just access then directly.
    >> >>
    >> >> It all seems to work fine, and has solved the strange delays my other
    >> >> webapp-users were experiencing.
    >> >>
    >> >> Does this sound like good practice?
    >> >>
    >> >> Thanks,
    >> >>
    >> >> John
    >> >>
    >> >>
    >> >
    >> >

    >>
    >>

    >
    >
     
    John, Sep 28, 2004
    #7
  8. John

    bruce barker Guest

    your code is safe, it just takes more server resources without any benefit.
    it no way makes the app quicker.

    the reason you cannot run another web app is because IE limits itself to 2
    connections to a server no matter how many browser instances you have. the
    other broswers are waiting for the request to complete, not the cpu to go
    down.

    -- bruce (sqlwork.com)



    "John" <> wrote in message
    news:u%...
    > LOL! the old ones are always the best!
    >
    > But my example demonstrates why I'm using a new thread - I don't want to

    do
    > two things at once in the same page in this instance, I just want to allow
    > other apps (and users on the same app) to run while my page is busy for a
    > few seconds.
    >
    > My code seems to fix the problem demonstrated in the example. I think ur
    > saying it's ok, and safe code! Just checking.
    >
    > John
    >
    >
    > "Kevin Spencer" <> wrote in message
    > news:...
    > > Hi John,
    > >
    > > I have used multi-threading in enterprise-level ASP.Net apps with no
    > > problems whatsoever. However, that wasn't my point. My point was that

    your
    > > use of Threading is equivalent to not using Threading at all, except for
    > > the
    > > extra overhead. Your app will respond in almost exactly the same time as
    > > if
    > > you hadn't used Threading at all.
    > >
    > > A program has a main execution thread, which is sequential. It executes
    > > exactly ONE instruction at a time. When a function is called, execution
    > > jumps to the function, and continues until the function exits, and the
    > > execution moves back to the calling function. Your technique does

    exactly
    > > that, but in a more complex manner. You spawn a thread, and put the
    > > current
    > > thread to sleep until it is finished processing.
    > >
    > > Think of it this way. You are a thread, sitting at home, going about

    your
    > > business, let's say, cleaning house. Now, you need to buy something from
    > > the
    > > store.
    > >
    > > Non-threaded: You go to the store to buy something. When you're done,

    you
    > > come home and go back to work.
    > >
    > > Your Threaded: You send your wife to the store to buy something. While
    > > she's
    > > gone, you take a nap. When she gets back, you go back to work.
    > >
    > > Properly Threaded: You send your wife to the store to buy something,

    while
    > > you continue working. When she gets back, you continue working.
    > >
    > > Note that the outcome of the first 2 situations is the same. You might
    > > just
    > > as well have gone to the store yourself. You accomplished the same

    amount
    > > of
    > > work in the time that you would have if you hadn't sent her at all.
    > >
    > > Okay, now imagine that you're not a thread, but a string. While your

    wife
    > > is
    > > gone to the store, you muss your hair until it is all matted and knotty.
    > > When your wife gets home, she doesn't recognize you, and cries "I

    thought
    > > you were a string!" You reply "I'm a frayed knot." ;-)
    > >
    > > --
    > > HTH,
    > > Kevin Spencer
    > > .Net Developer
    > > Microsoft MVP
    > > I get paid good money to
    > > solve puzzles for a living
    > >
    > > "John" <> wrote in message
    > > news:...
    > >> Thanks for the reply Kevin,
    > >>
    > >> Try writing a button click event handler (ASP.NET) that does:
    > >>
    > >> private void Button_Click()
    > >> {
    > >> DateTime d = DateTime.Now.AddSeconds(20);
    > >> while (DateTime.Now < d) ;
    > >> }
    > >>
    > >> Then try something like:
    > >>
    > >> private void AnotherButton_Click()
    > >> {
    > >> Thread th = new Thread(new ThreadStart(DoSomething));
    > >> th.Start();
    > >> th.Join();
    > >> }
    > >>
    > >> private void DoSomething()
    > >> {
    > >> DateTime d = DateTime.Now.AddSeconds(20);
    > >> while (DateTime.Now < d) ;
    > >> }
    > >>
    > >> Try to use other webapps while these are running.... weird!
    > >>
    > >> It's been discussed at length before (before anyone tells me it's

    stupid
    > > to
    > >> sit in a tight loop for 20 seconds!!!).
    > >>
    > >> In my real app I'm crunching numbers (entirely in C#, no SQL involved

    at
    > >> this stage). It only takes a couple of seconds (no chance of browser
    > >> timeout) but there seems to be a noticable performance change for other

    > > .NET
    > >> stuff on the server.
    > >>
    > >> Thanks,
    > >>
    > >> John
    > >>
    > >>
    > >>
    > >> "Kevin Spencer" <> wrote in message
    > >> news:...
    > >> > What you've designed is threading without any of the benefits of
    > >> > threading.
    > >> > You create a thread, and then sleep the main thread until it is

    > > finished.
    > >> > This is exactly equivalent to not creating a new thread at all. The
    > >> > purpose
    > >> > of threading is to allow 2 different tasks to be executed at the same
    > >> > time.
    > >> >
    > >> > --
    > >> > HTH,
    > >> > Kevin Spencer
    > >> > .Net Developer
    > >> > Microsoft MVP
    > >> > I get paid good money to
    > >> > solve puzzles for a living
    > >> >
    > >> > "John" <> wrote in

    message
    > >> > news:O#...
    > >> >> I've got some reasonably complex business logic in my C# code, in a

    > > class
    > >> >> called by a ASP.NET page. This takes around 3-4 seconds to execute.

    > > It's
    > >> > not
    > >> >> dependent on SQL calls or anything like that.
    > >> >>
    > >> >> I know of the problems with the worker process doing two things at
    > >> >> once
    > >> > from
    > >> >> the main thread pool (The old "Sit in a tight loop for 20 seconds

    and
    > >> > watch
    > >> >> all your webapps die!" issue). So I've worked around the issue by
    > >> >> spawning
    > >> >> the business logic into a new thread. The main thread Sleep()'s

    until
    > >> >> it's
    > >> >> finished, and then continues. eg:
    > >> >>
    > >> >> Button_Click:
    > >> >> 1) start new thread
    > >> >> 2) Sleep until it's done
    > >> >> 3) destroy it
    > >> >>
    > >> >> New thread:
    > >> >> 1) Do something heavy for a few seconds
    > >> >> 2) terminate
    > >> >>
    > >> >> I figured that, since the main thread is sleeping waiting for my new
    > >> > thread,
    > >> >> I don't need to lock() any objects, I can just access then directly.
    > >> >>
    > >> >> It all seems to work fine, and has solved the strange delays my

    other
    > >> >> webapp-users were experiencing.
    > >> >>
    > >> >> Does this sound like good practice?
    > >> >>
    > >> >> Thanks,
    > >> >>
    > >> >> John
    > >> >>
    > >> >>
    > >> >
    > >> >
    > >>
    > >>

    > >
    > >

    >
    >
     
    bruce barker, Sep 29, 2004
    #8
  9. John

    Scott Allen Guest

    These two strings walk into a bar.

    The first string says, "Bartender, I'd like a beer".
    The second string says "Bartender, I'd like a beer too adfj ack$#
    ickmd podsck".

    The first string says, "Bartender, you'll have to excuse my friend, he
    is not null terminated".

    Haha. Oops, I thought this was the C++ newsgroup...

    Anyway, John: I didn't see a noticeable difference between your two
    examples in my environment. Are you running in release mode and a
    release compile?

    --
    Scott
    http://www.OdeToCode.com/blogs/scott/
     
    Scott Allen, Sep 29, 2004
    #9
  10. John

    John Guest

    Hi Scott,

    Release mode: you mean just setting the combo-box in VS from 'Debug' to
    'Release', and changing web.config to debug="false"?? Then recompiling? (ok,
    I know the web.config thing is JIT and doesn't need a recompile...)

    Did that for both projects. Same behaviour. On 3 seperate machines. You have
    SMP on yours? Maybe that helps matters - provided you don't have more
    busy-threads than you have processors of course!

    Thanks,

    John

    "Scott Allen" <bitmask@[nospam].fred.net> wrote in message
    news:...
    > These two strings walk into a bar.
    >
    > The first string says, "Bartender, I'd like a beer".
    > The second string says "Bartender, I'd like a beer too adfj ack$#
    > ickmd podsck".
    >
    > The first string says, "Bartender, you'll have to excuse my friend, he
    > is not null terminated".
    >
    > Haha. Oops, I thought this was the C++ newsgroup...
    >
    > Anyway, John: I didn't see a noticeable difference between your two
    > examples in my environment. Are you running in release mode and a
    > release compile?
    >
    > --
    > Scott
    > http://www.OdeToCode.com/blogs/scott/
     
    John, Sep 29, 2004
    #10
  11. John

    John Guest

    Well, on my systems there is an obvious benefit. It doesn't improve the
    speed for person A, but it does stop him freezing out people B, C and D for
    a few seconds every time he initiates the business logic!

    It's quite easy to demonstrate in the code I've posted. Tested on 3 seperate
    servers.

    I don't think your idea about IE being the source of my problems is
    correct - otherwise why would the 2-thread version work fine, but the
    1-thread version doesn't?? Same test scenario. Same apparent behaviour for
    person A (the person initiating the long request). Very different behaviour
    for other users of the server.

    Thanks,

    John

    "bruce barker" <> wrote in message
    news:%...
    > your code is safe, it just takes more server resources without any
    > benefit.
    > it no way makes the app quicker.
    >
    > the reason you cannot run another web app is because IE limits itself to 2
    > connections to a server no matter how many browser instances you have. the
    > other broswers are waiting for the request to complete, not the cpu to go
    > down.
    >
    > -- bruce (sqlwork.com)
    >
    >
    >
    > "John" <> wrote in message
    > news:u%...
    >> LOL! the old ones are always the best!
    >>
    >> But my example demonstrates why I'm using a new thread - I don't want to

    > do
    >> two things at once in the same page in this instance, I just want to
    >> allow
    >> other apps (and users on the same app) to run while my page is busy for a
    >> few seconds.
    >>
    >> My code seems to fix the problem demonstrated in the example. I think ur
    >> saying it's ok, and safe code! Just checking.
    >>
    >> John
    >>
    >>
    >> "Kevin Spencer" <> wrote in message
    >> news:...
    >> > Hi John,
    >> >
    >> > I have used multi-threading in enterprise-level ASP.Net apps with no
    >> > problems whatsoever. However, that wasn't my point. My point was that

    > your
    >> > use of Threading is equivalent to not using Threading at all, except
    >> > for
    >> > the
    >> > extra overhead. Your app will respond in almost exactly the same time
    >> > as
    >> > if
    >> > you hadn't used Threading at all.
    >> >
    >> > A program has a main execution thread, which is sequential. It executes
    >> > exactly ONE instruction at a time. When a function is called, execution
    >> > jumps to the function, and continues until the function exits, and the
    >> > execution moves back to the calling function. Your technique does

    > exactly
    >> > that, but in a more complex manner. You spawn a thread, and put the
    >> > current
    >> > thread to sleep until it is finished processing.
    >> >
    >> > Think of it this way. You are a thread, sitting at home, going about

    > your
    >> > business, let's say, cleaning house. Now, you need to buy something
    >> > from
    >> > the
    >> > store.
    >> >
    >> > Non-threaded: You go to the store to buy something. When you're done,

    > you
    >> > come home and go back to work.
    >> >
    >> > Your Threaded: You send your wife to the store to buy something. While
    >> > she's
    >> > gone, you take a nap. When she gets back, you go back to work.
    >> >
    >> > Properly Threaded: You send your wife to the store to buy something,

    > while
    >> > you continue working. When she gets back, you continue working.
    >> >
    >> > Note that the outcome of the first 2 situations is the same. You might
    >> > just
    >> > as well have gone to the store yourself. You accomplished the same

    > amount
    >> > of
    >> > work in the time that you would have if you hadn't sent her at all.
    >> >
    >> > Okay, now imagine that you're not a thread, but a string. While your

    > wife
    >> > is
    >> > gone to the store, you muss your hair until it is all matted and
    >> > knotty.
    >> > When your wife gets home, she doesn't recognize you, and cries "I

    > thought
    >> > you were a string!" You reply "I'm a frayed knot." ;-)
    >> >
    >> > --
    >> > HTH,
    >> > Kevin Spencer
    >> > .Net Developer
    >> > Microsoft MVP
    >> > I get paid good money to
    >> > solve puzzles for a living
    >> >
    >> > "John" <> wrote in message
    >> > news:...
    >> >> Thanks for the reply Kevin,
    >> >>
    >> >> Try writing a button click event handler (ASP.NET) that does:
    >> >>
    >> >> private void Button_Click()
    >> >> {
    >> >> DateTime d = DateTime.Now.AddSeconds(20);
    >> >> while (DateTime.Now < d) ;
    >> >> }
    >> >>
    >> >> Then try something like:
    >> >>
    >> >> private void AnotherButton_Click()
    >> >> {
    >> >> Thread th = new Thread(new ThreadStart(DoSomething));
    >> >> th.Start();
    >> >> th.Join();
    >> >> }
    >> >>
    >> >> private void DoSomething()
    >> >> {
    >> >> DateTime d = DateTime.Now.AddSeconds(20);
    >> >> while (DateTime.Now < d) ;
    >> >> }
    >> >>
    >> >> Try to use other webapps while these are running.... weird!
    >> >>
    >> >> It's been discussed at length before (before anyone tells me it's

    > stupid
    >> > to
    >> >> sit in a tight loop for 20 seconds!!!).
    >> >>
    >> >> In my real app I'm crunching numbers (entirely in C#, no SQL involved

    > at
    >> >> this stage). It only takes a couple of seconds (no chance of browser
    >> >> timeout) but there seems to be a noticable performance change for
    >> >> other
    >> > .NET
    >> >> stuff on the server.
    >> >>
    >> >> Thanks,
    >> >>
    >> >> John
    >> >>
    >> >>
    >> >>
    >> >> "Kevin Spencer" <> wrote in message
    >> >> news:...
    >> >> > What you've designed is threading without any of the benefits of
    >> >> > threading.
    >> >> > You create a thread, and then sleep the main thread until it is
    >> > finished.
    >> >> > This is exactly equivalent to not creating a new thread at all. The
    >> >> > purpose
    >> >> > of threading is to allow 2 different tasks to be executed at the
    >> >> > same
    >> >> > time.
    >> >> >
    >> >> > --
    >> >> > HTH,
    >> >> > Kevin Spencer
    >> >> > .Net Developer
    >> >> > Microsoft MVP
    >> >> > I get paid good money to
    >> >> > solve puzzles for a living
    >> >> >
    >> >> > "John" <> wrote in

    > message
    >> >> > news:O#...
    >> >> >> I've got some reasonably complex business logic in my C# code, in a
    >> > class
    >> >> >> called by a ASP.NET page. This takes around 3-4 seconds to execute.
    >> > It's
    >> >> > not
    >> >> >> dependent on SQL calls or anything like that.
    >> >> >>
    >> >> >> I know of the problems with the worker process doing two things at
    >> >> >> once
    >> >> > from
    >> >> >> the main thread pool (The old "Sit in a tight loop for 20 seconds

    > and
    >> >> > watch
    >> >> >> all your webapps die!" issue). So I've worked around the issue by
    >> >> >> spawning
    >> >> >> the business logic into a new thread. The main thread Sleep()'s

    > until
    >> >> >> it's
    >> >> >> finished, and then continues. eg:
    >> >> >>
    >> >> >> Button_Click:
    >> >> >> 1) start new thread
    >> >> >> 2) Sleep until it's done
    >> >> >> 3) destroy it
    >> >> >>
    >> >> >> New thread:
    >> >> >> 1) Do something heavy for a few seconds
    >> >> >> 2) terminate
    >> >> >>
    >> >> >> I figured that, since the main thread is sleeping waiting for my
    >> >> >> new
    >> >> > thread,
    >> >> >> I don't need to lock() any objects, I can just access then
    >> >> >> directly.
    >> >> >>
    >> >> >> It all seems to work fine, and has solved the strange delays my

    > other
    >> >> >> webapp-users were experiencing.
    >> >> >>
    >> >> >> Does this sound like good practice?
    >> >> >>
    >> >> >> Thanks,
    >> >> >>
    >> >> >> John
    >> >> >>
    >> >> >>
    >> >> >
    >> >> >
    >> >>
    >> >>
    >> >
    >> >

    >>
    >>

    >
    >
     
    John, Sep 29, 2004
    #11
  12. Someone from Microsoft might want to jump in here and correct me if my guess
    is wrong.

    I can explain the behavior, but I'm not sure if this explanation is accurate
    or not. This would explain 100% both the "lock while work is going on" and
    the reason the thread + sleep combination works. However, it makes an
    assumtion about the working process' implementation that I do not know is
    valid.

    IIS uses fibers. A fiber is a unit smaller than a thread that cooperatively
    multitasks within a thread. The thread can be pre-empted, but a fiber within
    the thread cannot. A fiber runs until it executes a blocking operation,
    yields or completes.

    A fiber is like a thread in how it works -- you write a function (delegate)
    and start up the fiber. The fiber runs in the current thread's time slice
    until it hits a blocking operation, when it hits a blocking operation another
    fiber in the current thread is selected and given time.

    You use this a lot in TCP protocols. If you start a thread for each user,
    then you consume a lot of system resources and realy slow down the computer a
    lot. If you use a single thread, then you have to do a lot of work keeping
    straight which user is which. So you have a choice between simple and
    elegant code (threading) or code that is faster, but that requires a lot more
    work.

    Fibers are an "inbetween" approach. Fiber code looks just like multiple
    threads, but consumes resources as if it were single threaded. The code runs
    exclusively within its thread until it hits a blocking operation, then
    another block of code within the thread is given time.

    The issue is if the fiber never hits a blocking operation, it starves the
    other fibers.

    You don't want to sleep, in this case, because sleep just yields to the next
    fiber or thread. You want to join your thread you launched, which should
    have the same effect but ensures the order in which events occur. I would
    not want to just sleep, I would want to call Thread.Join.

    The alternative approach is that you add a Sleep(0) call periodically in the
    work that you're doing, in order to give other fibers a chance to execute.

    "John" wrote:

    > Hi Scott,
    >
    > Release mode: you mean just setting the combo-box in VS from 'Debug' to
    > 'Release', and changing web.config to debug="false"?? Then recompiling? (ok,
    > I know the web.config thing is JIT and doesn't need a recompile...)
    >
    > Did that for both projects. Same behaviour. On 3 seperate machines. You have
    > SMP on yours? Maybe that helps matters - provided you don't have more
    > busy-threads than you have processors of course!
    >
    > Thanks,
    >
    > John
    >
    > "Scott Allen" <bitmask@[nospam].fred.net> wrote in message
    > news:...
    > > These two strings walk into a bar.
    > >
    > > The first string says, "Bartender, I'd like a beer".
    > > The second string says "Bartender, I'd like a beer too adfj ack$#
    > > ickmd podsck".
    > >
    > > The first string says, "Bartender, you'll have to excuse my friend, he
    > > is not null terminated".
    > >
    > > Haha. Oops, I thought this was the C++ newsgroup...
    > >
    > > Anyway, John: I didn't see a noticeable difference between your two
    > > examples in my environment. Are you running in release mode and a
    > > release compile?
    > >
    > > --
    > > Scott
    > > http://www.OdeToCode.com/blogs/scott/

    >
    >
    >
     
    =?Utf-8?B?RGF2ZSBCYWNoZXI=?=, Oct 1, 2004
    #12
  13. John

    Scott Allen Guest

    Hi Dave:

    That's an interesting thought. AFAIK, though, SQL Server is the only
    product to use fibers. The ASP.NET runtime executes in a process
    outside of IIS, and I've never read about it using fibers either, do
    you have a source of info?

    --
    Scott
    http://www.OdeToCode.com/


    On Fri, 1 Oct 2004 15:37:03 -0700, "Dave Bacher"
    <> wrote:

    >Someone from Microsoft might want to jump in here and correct me if my guess
    >is wrong.
    >
    >I can explain the behavior, but I'm not sure if this explanation is accurate
    >or not. This would explain 100% both the "lock while work is going on" and
    >the reason the thread + sleep combination works. However, it makes an
    >assumtion about the working process' implementation that I do not know is
    >valid.
    >
    >IIS uses fibers. A fiber is a unit smaller than a thread that cooperatively
    >multitasks within a thread. The thread can be pre-empted, but a fiber within
    >the thread cannot. A fiber runs until it executes a blocking operation,
    >yields or completes.
    >
    >A fiber is like a thread in how it works -- you write a function (delegate)
    >and start up the fiber. The fiber runs in the current thread's time slice
    >until it hits a blocking operation, when it hits a blocking operation another
    >fiber in the current thread is selected and given time.
    >
    >You use this a lot in TCP protocols. If you start a thread for each user,
    >then you consume a lot of system resources and realy slow down the computer a
    >lot. If you use a single thread, then you have to do a lot of work keeping
    >straight which user is which. So you have a choice between simple and
    >elegant code (threading) or code that is faster, but that requires a lot more
    >work.
    >
    >Fibers are an "inbetween" approach. Fiber code looks just like multiple
    >threads, but consumes resources as if it were single threaded. The code runs
    >exclusively within its thread until it hits a blocking operation, then
    >another block of code within the thread is given time.
    >
    >The issue is if the fiber never hits a blocking operation, it starves the
    >other fibers.
    >
    >You don't want to sleep, in this case, because sleep just yields to the next
    >fiber or thread. You want to join your thread you launched, which should
    >have the same effect but ensures the order in which events occur. I would
    >not want to just sleep, I would want to call Thread.Join.
    >
    >The alternative approach is that you add a Sleep(0) call periodically in the
    >work that you're doing, in order to give other fibers a chance to execute.
    >
    >"John" wrote:
    >
    >> Hi Scott,
    >>
    >> Release mode: you mean just setting the combo-box in VS from 'Debug' to
    >> 'Release', and changing web.config to debug="false"?? Then recompiling? (ok,
    >> I know the web.config thing is JIT and doesn't need a recompile...)
    >>
    >> Did that for both projects. Same behaviour. On 3 seperate machines. You have
    >> SMP on yours? Maybe that helps matters - provided you don't have more
    >> busy-threads than you have processors of course!
    >>
    >> Thanks,
    >>
    >> John
    >>
    >> "Scott Allen" <bitmask@[nospam].fred.net> wrote in message
    >> news:...
    >> > These two strings walk into a bar.
    >> >
    >> > The first string says, "Bartender, I'd like a beer".
    >> > The second string says "Bartender, I'd like a beer too adfj ack$#
    >> > ickmd podsck".
    >> >
    >> > The first string says, "Bartender, you'll have to excuse my friend, he
    >> > is not null terminated".
    >> >
    >> > Haha. Oops, I thought this was the C++ newsgroup...
    >> >
    >> > Anyway, John: I didn't see a noticeable difference between your two
    >> > examples in my environment. Are you running in release mode and a
    >> > release compile?
    >> >
    >> > --
    >> > Scott
    >> > http://www.OdeToCode.com/blogs/scott/

    >>
    >>
    >>
     
    Scott Allen, Oct 2, 2004
    #13
  14. Hi Scott,

    No inside source.

    Keep in mind I've been running Windows a very long time. I have a copy of
    Windows 1.04, 2.0, 3.0, 3.1, 3.11, 3.11wg (and yes the last two are subtlely
    different), OS/2 3, OS/2 4, 95a, 98a, 98se, Me, 2000 and XP. I've written
    and tested software on all versions of NT, including 3.1, as well, and have
    written a small amount of Windows software for a very, very long time.

    On OS/2, I used multithreading to take a program that was crawling along at
    less than half the speed of the Windows version to get it running at 3x the
    speed of the Windows version, so I'm very familiar with multithreading and
    multithreaded design.

    This is just a guess as to how the system operates, without investigating
    with a debugger, all I can do is guess by observing behavior. I can provide
    two explanations as to how IIS would block on ASP.NET requests, which it can
    be verified that it does. As fibers are a "Microsoft Recommended" mechanism
    for dealing with out of process blocking events, such as COM calls, I think
    it is likely that fibers probably are in play here in some way, shape or
    form, but no, I cannot prove that.

    The best two explanations that I can think of are the fiber approach I
    posted earlier. This seems likely as one of the goals with XP was to improve
    IIS's throughput, and everyone knows how much fiber helps with throughput...
    just kidding, but seriously, with TCP based protocols (such as TDS, which you
    mention, and HTTP), fibers can improve performance a lot.

    The other explanation is that aspnet_wp is invoked via COM, and is
    configured to only allow a single COM request at a time. This would be a
    spectacularly bad design, contrary to most of Microsoft's stated design goals
    -- however Microsoft's been known to use poor design now and again (Windows
    9x, Windows Me, need I continue...), and so it can't be ruled out.

    In the case of a poorly implemented out of process COM control, what would
    happen is that the parent's post would start a thread, and then return. When
    control returned to the IIS process, IIS would be able to process the next
    request as the thread continues its work, and the sleep statement would be
    irrelevant (and could be removed).

    However, this could be easily verified. Just do Sleep(20000), if you get a
    pause for 20 seconds, then you know it isn't fibers. If the computer
    continues, then you know it is fibers. Likewise, you can pop a sleep in a
    loop and see what happens.


    "Scott Allen" wrote:

    > Hi Dave:
    >
    > That's an interesting thought. AFAIK, though, SQL Server is the only
    > product to use fibers. The ASP.NET runtime executes in a process
    > outside of IIS, and I've never read about it using fibers either, do
    > you have a source of info?
    >
    > --
    > Scott
    > http://www.OdeToCode.com/
    >
    >
    > On Fri, 1 Oct 2004 15:37:03 -0700, "Dave Bacher"
    > <> wrote:
    >
    > >Someone from Microsoft might want to jump in here and correct me if my guess
    > >is wrong.
    > >
    > >I can explain the behavior, but I'm not sure if this explanation is accurate
    > >or not. This would explain 100% both the "lock while work is going on" and
    > >the reason the thread + sleep combination works. However, it makes an
    > >assumtion about the working process' implementation that I do not know is
    > >valid.
    > >
    > >IIS uses fibers. A fiber is a unit smaller than a thread that cooperatively
    > >multitasks within a thread. The thread can be pre-empted, but a fiber within
    > >the thread cannot. A fiber runs until it executes a blocking operation,
    > >yields or completes.
    > >
    > >A fiber is like a thread in how it works -- you write a function (delegate)
    > >and start up the fiber. The fiber runs in the current thread's time slice
    > >until it hits a blocking operation, when it hits a blocking operation another
    > >fiber in the current thread is selected and given time.
    > >
    > >You use this a lot in TCP protocols. If you start a thread for each user,
    > >then you consume a lot of system resources and realy slow down the computer a
    > >lot. If you use a single thread, then you have to do a lot of work keeping
    > >straight which user is which. So you have a choice between simple and
    > >elegant code (threading) or code that is faster, but that requires a lot more
    > >work.
    > >
    > >Fibers are an "inbetween" approach. Fiber code looks just like multiple
    > >threads, but consumes resources as if it were single threaded. The code runs
    > >exclusively within its thread until it hits a blocking operation, then
    > >another block of code within the thread is given time.
    > >
    > >The issue is if the fiber never hits a blocking operation, it starves the
    > >other fibers.
    > >
    > >You don't want to sleep, in this case, because sleep just yields to the next
    > >fiber or thread. You want to join your thread you launched, which should
    > >have the same effect but ensures the order in which events occur. I would
    > >not want to just sleep, I would want to call Thread.Join.
    > >
    > >The alternative approach is that you add a Sleep(0) call periodically in the
    > >work that you're doing, in order to give other fibers a chance to execute.
    > >
    > >"John" wrote:
    > >
    > >> Hi Scott,
    > >>
    > >> Release mode: you mean just setting the combo-box in VS from 'Debug' to
    > >> 'Release', and changing web.config to debug="false"?? Then recompiling? (ok,
    > >> I know the web.config thing is JIT and doesn't need a recompile...)
    > >>
    > >> Did that for both projects. Same behaviour. On 3 seperate machines. You have
    > >> SMP on yours? Maybe that helps matters - provided you don't have more
    > >> busy-threads than you have processors of course!
    > >>
    > >> Thanks,
    > >>
    > >> John
    > >>
    > >> "Scott Allen" <bitmask@[nospam].fred.net> wrote in message
    > >> news:...
    > >> > These two strings walk into a bar.
    > >> >
    > >> > The first string says, "Bartender, I'd like a beer".
    > >> > The second string says "Bartender, I'd like a beer too adfj ack$#
    > >> > ickmd podsck".
    > >> >
    > >> > The first string says, "Bartender, you'll have to excuse my friend, he
    > >> > is not null terminated".
    > >> >
    > >> > Haha. Oops, I thought this was the C++ newsgroup...
    > >> >
    > >> > Anyway, John: I didn't see a noticeable difference between your two
    > >> > examples in my environment. Are you running in release mode and a
    > >> > release compile?
    > >> >
    > >> > --
    > >> > Scott
    > >> > http://www.OdeToCode.com/blogs/scott/
    > >>
    > >>
    > >>

    >
    >
     
    =?Utf-8?B?RGF2ZSBCYWNoZXI=?=, Oct 7, 2004
    #14
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alina
    Replies:
    0
    Views:
    1,683
    Alina
    Jul 16, 2003
  2. Sam
    Replies:
    2
    Views:
    368
  3. Replies:
    9
    Views:
    1,047
    Mark Space
    Dec 29, 2007
  4. Steven Woody
    Replies:
    0
    Views:
    422
    Steven Woody
    Jan 9, 2009
  5. Steven Woody
    Replies:
    0
    Views:
    456
    Steven Woody
    Jan 9, 2009
Loading...

Share This Page