none
Multi-Threading and Dual-Core Processors RRS feed

  • Question

  • Hi all,

    I have written a simple VB.Net Windows Forms application that spawns a bunch of threads that run concurrently for about a minute. My development machine is a Dell XPS system which features a Pentium D dual-core processor.
    When I run my application on this machine, task manager shows that only one of the cores is being used (at 100%). The other core is not doing any work at all (between 0 and 10% -What little activity there is is probably due to other processes running on my machine like media player, anti virus etc.).
    My question is: Is there a problem with my code, my CPU, my OS? Do I have to explicitly tell my code to take advantage of two processing units?
    Thank you,

    Vince
    Friday, March 7, 2008 9:34 PM

Answers

  • I'm leaning toward your code, at the moment.  I've successfully used threads to spread work across multiple CPUs.

    How are you creating the threads?

    Note that if you're modifying your process or threads' affinity mask, it'll affect what cores they will use, but this isn't something someone would do on accident...

    -Ryan / Kardax
    Friday, March 7, 2008 11:24 PM
  • I'm glad I could at least be the pathway to your answer Smile

    As for your other question, there's no harm in threads spawning threads.  They're each totally independant entities, so it really doesn't make a difference how each thread comes into existence.

    There is a bit of overhead in creating the threads in the first place, so if you're creating lots and lots of them, you may find some performance gained by merging the workload into a smaller number of threads.

    -Ryan / Kardax
    Saturday, March 8, 2008 2:44 AM

All replies

  • I'm leaning toward your code, at the moment.  I've successfully used threads to spread work across multiple CPUs.

    How are you creating the threads?

    Note that if you're modifying your process or threads' affinity mask, it'll affect what cores they will use, but this isn't something someone would do on accident...

    -Ryan / Kardax
    Friday, March 7, 2008 11:24 PM
  • Hi Ryan,

    Thanks for your quick reply.
    After spending 40 minutes trying to clean up my code and clearly explain what I am trying to do (the "Code Snippet" control is not very cooperative...), I realized my mistake: The threads I am spawning are not monopolizing the CPU. The main thread (which spawns the sub threads) is monopolizing the CPU. I'm sure this is far from clear, but thanks for your help. Without your reply, I would not have tried to explain my code, and I would not have fully remembered, understood and analyzed it.

    I'm going to try a different approach to the thread-spawning and see if that helps at all or if I'm still in the same situation.

    Thanks again,

     

    Vince

    BTW: Is it bad mojo to have a thread spawn a thread spawn a thread? Basically a recursive algorithm that spawns a thread for each recursion?

    Saturday, March 8, 2008 12:09 AM
  • I'm glad I could at least be the pathway to your answer Smile

    As for your other question, there's no harm in threads spawning threads.  They're each totally independant entities, so it really doesn't make a difference how each thread comes into existence.

    There is a bit of overhead in creating the threads in the first place, so if you're creating lots and lots of them, you may find some performance gained by merging the workload into a smaller number of threads.

    -Ryan / Kardax
    Saturday, March 8, 2008 2:44 AM
  • Hi Ryan,

     

    I built another app just to test the threading. I'm getting the same results. The app starts 100 threads that do some processing on a large string. I have about 30 threads running at any given time but still only one core at full usage and the other core with no work being done.

    My machine is a Dell XPS 400 with Windows XP Media Center Edition - Pentium D 830 at 3 GHz and 2 GB RAM.

    Any ideas?

    Thanks again,

     

    The test app is a windows form application with one form with the following controls:

    txtThreads - A textbox for the user to indicate the number of threads to start

    btnStart - A button to start a new test

    btnStop - A button to stop the test in progress

    txtOutput - A textbox with output information (thread start and completion are logged here)

    lblStatus - A label to indicate current status

     

    This is the code behind the form in case you want to test it out:

    Code Snippet

    Public Class Form1

    Shared blnContinue As Boolean = False

    Shared intThreads As Integer = 0

    Private strAlphabet() As String = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}

    Private strData As String = ""

    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click

    Dim t As clsThread = Nothing

    Try

    blnContinue = True

    Me.btnStart.Enabled = False

    Me.btnStop.Enabled = True

    'Reset output textbox

    txtOutput.Text = ""

    If strData = "" Then

    'Build Data string

    Dim stbData As New System.Text.StringBuilder()

    txtOutput.Text = ""

    Application.DoEvents()

    'Build a string of 500000 uppercase characters

    For i As Integer = 0 To 500000

    If Not blnContinue Then

    'User asked to stop

    Exit For

    End If

    stbData.Append(strAlphabet(i Mod 26))

    If i Mod 100 = 0 Then

    txtOutput.Text = "Building data string - " & (i + 1) & "/" & 500000

    Application.DoEvents()

    End If

    Next

    strData = stbData.ToString()

    txtOutput.Text = " OK" & ControlChars.CrLf

    Application.DoEvents()

    End If

    'Start processing threads - As many as specified in txtThreads textbox

    For i As Integer = 0 To Val(txtThreads.Text) - 1

    If Not blnContinue Then

    'User asked to stop

    Exit For

    End If

    t = New clsThread("Thread #" & (i + 1) & " of " & txtThreads.Text, strData)

    AddHandler t.DoneProcessing, AddressOf thr_DoneProcessing

    t.StartProcessing()

    txtOutput.AppendText("Started Thread #" & (i + 1) & " of " & txtThreads.Text & ControlChars.CrLf)

    txtOutput.Select(txtOutput.TextLength - 1, 0)

    txtOutput.ScrollToCaret()

    Application.DoEvents()

    Next

    'Wait for all threads to complete

    While intThreads > 0

    System.Threading.Thread.Sleep(100)

    lblStatus.Text = "Waiting for " & intThreads & " Threads to complete..."

    Application.DoEvents()

    End While

    'Done

    txtOutput.AppendText("Complete" & ControlChars.CrLf)

    lblStatus.Text = "Complete"

    Catch ex As Exception

    'There was an error

    MessageBox.Show("btnStart_Click() Failed: " & ControlChars.CrLf & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)

    Finally

    blnContinue = False

    Me.btnStart.Enabled = True

    Me.btnStop.Enabled = False

    End Try

    End Sub

    Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click

    Try

    blnContinue = False

    Catch ex As Exception

    'There was an error

    MessageBox.Show("btnStop_Click() Failed: " & ControlChars.CrLf & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)

    End Try

    End Sub

    Private Delegate Sub thr_DoneProcessingDelegate(ByVal strMessage As String)

    Public Sub thr_DoneProcessing(ByVal strMessage As String)

    If Me.txtOutput.InvokeRequired Then

    Dim args() As Object = {strMessage}

    Me.txtOutput.Invoke(New thr_DoneProcessingDelegate(AddressOf thr_DoneProcessing), args)

    Else

    txtOutput.AppendText(strMessage & ControlChars.CrLf)

    txtOutput.Select(txtOutput.TextLength - 1, 0)

    txtOutput.ScrollToCaret()

    Application.DoEvents()

    lblStatus.Text = intThreads & " Threads in progress"

    End If

    End Sub

    Public Class clsThread

    Private strData As String = ""

    Private strName As String = ""

    Private thr As System.Threading.Thread = Nothing

    Public Event DoneProcessing(ByVal strM As String)

    Public Property Data() As String

    Get

    Return strData

    End Get

    Set(ByVal value As String)

    strData = value

    End Set

    End Property

    Public Sub New(Optional ByVal nam As String = "", Optional ByVal dat As String = "")

    Me.strName = nam

    Me.strData = dat

    End Sub

    Public Sub Process()

    Dim intAs As Integer = 0

    Try

    Dim datStart As DateTime = Date.Now()

    For i As Integer = 0 To strData.Length - 1

    If Not blnContinue Then

    'User asked to stop

    Exit For

    End If

    If strData.Substring(i, 1) = "A" Then

    If i Mod 10 = 0 Then

    'Slow the thread down to make sure at least 2 threads are running at the same time

    Application.DoEvents()

    End If

    intAs += 1

    End If

    Next

    Dim strMessage As String = Me.strName & " Found " & intAs & " 'A's in " & (New TimeSpan(Date.Now.Ticks - datStart.Ticks)).TotalSeconds.ToString("0.000")

    RaiseEvent DoneProcessing(strMessage)

    Catch ex As Exception

    'There was an error

    RaiseEvent DoneProcessing(strName & " - clsThread.Process() Failed: " & ControlChars.CrLf & ex.Message)

    Finally

    intThreads -= 1

    End Try

    End Sub

    Public Sub StartProcessing()

    Try

    thr = New System.Threading.Thread(AddressOf Process)

    intThreads += 1

    thr.Start()

    Catch ex As Exception

    'There was an error

    Throw New Exception("clsThread.StartProcessing() Failed: " & ControlChars.CrLf & ex.Message)

    End Try

    End Sub

    End Class

    End Class

     

     

     

    Monday, March 10, 2008 6:00 AM
  • More info: I tested the same code on one of the machines at work, Windows 2003 64-bit on an Intel Xeon processor, and all four cores are in use. I think my issue one my home computer might be OS-related (WinXP Media Center Edition). Has ayone heard of anything like this?
    Thanks,

    Vince
    Monday, March 10, 2008 2:34 PM
  • I haven't heard of this before, but it seems possible; the OS is reponsible for deciding which threads go on which CPUs and evidently the relatively crippled Media Center Edition isn't making a good choice here.

    You might be able to change the threads' affinity mask value to force them to specific CPU cores, but since .NET threads don't provide this option, you'd have to try to match the Process (System.Diagnostics namespace) threads to the .NET threads, and go from there...

    -Ryan / Kardax
    Monday, March 10, 2008 3:31 PM
  • The Media Center Edition is an enhanced XP Professional OS.  You code uses both cores or my system.

    Monday, March 10, 2008 4:56 PM
  • Hi John,

    Did you run this without a debugger attached? The debugger does its own processing that actually shows activity on my second core, but when I run the compiled executable is when the second code doesn't do any work.
    If your computer is running these threads on both cores, then what could the issue be in your opinion? A configuration issue on the OS or BIOS level? It seems that the CPU and motherboard are fine because I do (occasionally) see activity on the second core, just not when running my code...
    Thanks,

    Vince
    Monday, March 10, 2008 9:05 PM
  • Running this code in the debugger or the exe doesn't make much difference.  It uses both cores on three of my dual core machines, including the Media Center system.

    Code Snippet

    Public Class Form1

      Dim MeClose As Boolean

      Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing

        MeClose = True

      End Sub

      Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown

        BackgroundWorker1.WorkerReportsProgress = True

        For I As Integer = 0 To 999999

          TextBox2.Text = I.ToString

          TextBox2.Refresh()

          While BackgroundWorker1.IsBusy

            Application.DoEvents()

          End While

          BackgroundWorker1.RunWorkerAsync()

          If MeClose Then Exit For

        Next

      End Sub

      Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

        Static I As Integer

        BackgroundWorker1.ReportProgress(I)

        I += 1

      End Sub

      Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged

        TextBox1.Text = e.ProgressPercentage.ToString

        TextBox1.Refresh()

      End Sub

    End Class

     

    Monday, March 10, 2008 10:45 PM
  • umm, maybe on a machine you use the windows option to give hight priority to UI threads, it's only a hipothesis but it can explain why your behaviour is different on different machine

     


    Tuesday, March 11, 2008 2:40 PM
  • Hi Korkless,

     

    When I saw your post, I thought "Of course! Why didn't I think of that?" But after testing out the different options in "Processor Scheduling", I didn't get better results.

    I ended up installing XP Pro over my XP Media Center Edition and now my code is using both cores...

    Not really sure what the problem was, I have heard/read that MCE is XP Pro + more features, so it seems that they should behave similarly on such a low-level as processor/thread scheduling...

    Thanks for all your help everyone.

     

    Vince

    Wednesday, March 12, 2008 3:56 PM
  • I did find a discussion about a problem like this here: http://forum.notebookreview.com/showthread.php?t=60416

    Apparently, there's a hot fix for Windows XP that enables proper dual-core support.  I would think that would come down as a Windows Update, though... did you ever use Windows Update on your MCE box?  I suppose it doesn't matter now that you've installed Pro...

    -Ryan / Kardax
    Thursday, March 13, 2008 10:07 PM
  • Hi Ryan,

    I actually did step through that article before posting on the forums here. Yes, my PC did have all windows updates (even optional ones!).
    The machine had been acting goofy for a while. There were a few display issues and warnings about dlls every now and then, so reinstalling was inevitable anyway.
    Thanks again,

    Vince
    Friday, March 14, 2008 1:57 PM