Click here to Skip to main content
Click here to Skip to main content

A new approach to multithreading?

, , 3 Sep 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
A strange idea I had for doing multithreading more managable.

Introduction

We have to admit, multithreading is hard to understand. On the other hand, if you want to do more in less time, MT is the way to go. I would like to present a concept/pattern I developed to make MT easier in some scenarios. 

Background

Under the introduction to CQRS in our company I wanted to fill all dependent properties on a DTO in parallel.

Using the Code

The 'old way' of doing this in a sequential manner would be something like this, very simplified of course

    Public Class Person
        Public Property Id As Integer
        Public Property Name As String

        Public Property Email As IEnumerable(Of EMail)
        Public Property Address As IEnumerable(Of Address)
    End Class

    Public Class EMail
        Public Property Id As Integer
        Public Property PersonId As Integer
        Public Property Adress As String
    End Class
    Public Class Address
        Public Property Id As Integer
        Public Property PersonId As Integer
        Public Property Address As String
    End Class
 Public Class GetPersonInfo
       
        Public ConnectionInfo As New ServerConnectionInfo With {.Address = "sql", .Database = "person", .UserName = "sa", .Password = "*******"} 'Don't worry about this.. 

        Public shared Sub FillPerson(id as integer,byref p as person)
            Dim cmd As New CommandInfo
            cmd.Parameters.Add("Id", DbType.Int32, Query.Id)
            cmd.CommandText = "Select * from Person where Id = @Id "
            Data.Exec(ConnectionInfo, cmd, Result)
        End Sub

        Public Shared Sub FillEmail(id as integer,byref l as iEnumerable(of Email))
            Dim list As New List(Of EMail)
            Dim cmd As New CommandInfo
            cmd.Parameters.Add("Id", DbType.Int32, Query.Id)
            cmd.CommandText = "Select * from HrElectronicAddress where unitId = @Id "
            Data.Exec(ConnectionInfo, cmd, list)
            l = list
        End Sub

        Public Shared Sub FillAddress()
            Dim list As New List(Of Addresss)
            Dim cmd As New CommandInfo
            cmd.Parameters.Add("Id", DbType.Int32, Query.Id)
            cmd.CommandText = "Select * from HrAddress where unitId = @Id "
            Data.Exec(ConnectionInfo, cmd, list)
            l = list
        End Sub

    End Class
'To use this
Dim p as new Person
dim Id as integer = 1
GetPersonInfo.FillPerson(Id,p))
GetPersonInfo.FillEmail(Id,p.Email))
GetPersonInfo.FillAddress(Id,p.Address))

My goal was to re-use much of the same logic for filling, but be able to do it in parallel. 

First of all, we have to do some wrapping of the functions.

This is just an Interface and a baseclass to be able to identify the multithreaded queryhandlers. 

    Public Interface IParalellQuery
        ReadOnly Property InnerResult As Object
        Property InnerQuery As Object
    End Interface

Public MustInherit Class ParalellQueryBase(Of TQuery As IAmAQuery, TResult As New)
        Implements IParalellQuery

        Public Query As TQuery
        Public Result As TResult

        Public Sub New()
            Result = New TResult
        End Sub

        Friend Property InnerQuery As Object Implements IParalellQuery.InnerQuery
            Set(value As Object)
                Query = CType(value, TQuery)
            End Set
            Get
                Return Query
            End Get
        End Property

        Public ReadOnly Property InnerResult As Object Implements IParalellQuery.InnerResult
            Get
                Return Result
            End Get
        End Property
    End Class
Public Class GetPersonInfoQuery
        Inherits QueryBase

        Public Property Id As Integer

        Public Overrides ReadOnly Property Name As String
            Get
                Return "GetPersonInfo"
            End Get
        End Property
    End Class

The same as above but prepared for multithreading..

Public Class GetPersonInfoQueryHandler
       Inherits ParalellQueryBase(Of GetPersonInfoQuery, Person)
       Public ConnectionInfo As New ServerConnectionInfo With {.Address = "sql", .Database = "person", .UserName = "sa", .Password = "*******"} 'Don't worry about this.. 

        Public Sub FillPerson()
            Dim cmd As New CommandInfo
            cmd.Parameters.Add("Id", DbType.Int32, Query.Id)
            cmd.CommandText = "Select * from Person where Id = @Id "
            Data.Exec(ConnectionInfo, cmd, Result)
        End Sub

        Public Sub FillEmail()
            Dim list As New List(Of EMail)
            Dim cmd As New CommandInfo
            cmd.Parameters.Add("Id", DbType.Int32, Query.Id)
            cmd.CommandText = "Select * from HrElectronicAddress where unitId = @Id "
            Data.Exec(ConnectionInfo, cmd, list)
            Result.Email = list
        End Sub

        Public Sub FillAddress()
            Dim list As New List(Of Addresss)
            Dim cmd As New CommandInfo
            cmd.Parameters.Add("Id", DbType.Int32, Query.Id)
            cmd.CommandText = "Select * from HrAddress where unitId = @Id "
            Data.Exec(ConnectionInfo, cmd, list)
            Result.Addresss = list
        End Sub

    End Class

The Result property used, is from the baseclass, and is of the correct type thanks to the generic type parameter. 

The parameters needed inside the Sub's is passed through the GetPersoninfoQuery class. In our case, containing just the Id of the person. 

How to invoke this?

Dim res As Person
Dim q As New GetPersonInfoQuery

    q.Id = 1
    res = q.Execute(Of Person)()

Here is the main code for calling the subs on the handler. 

           If MultiHandlers.ContainsKey(q.GetType) Then
                Dim handler = MultiHandlers(q.GetType)
                Dim target As Query.IParalellQuery = CType(handler.CreateInstance, IParalellQuery)
                target.InnerQuery = q

                handler.Methods.AsParallel.ForAll(Sub(m)
                                                      m.Invoke(target, {})
                                                  End Sub)
                Return target.InnerResult
            End If

Some explanations needed here. 

All classes implementing the IMultiquery interface is found in the application and there the system builds and index to know which class handles which Query. The target her is an instace of the GetPersonInfoQueryHandler.

Alle public sub's on the handler type is extracted using reflection and inserted into the Methods property of the handler, then using the AsParallel.ForAll extension method to invoke all sub on the target.

Points of Interest

Did this actually improve anything? Well the speed is better.

<Test> Public Sub SpeedTest()
        Dim tinfo As New Utils.TimingInfos
        Dim res As Person
        Dim q As New GetPersonInfoQuery
        q.Id = 1

        res = CType(Query.Handling.ExecuteQuery(q), Person)
        
        Using New Utils.InlineTimer("Paralell", tinfo)
            For x = 0 To 100
                res = CType(Query.Handling.ExecuteQuery(q), Person)
            Next
        End Using
        Using New Utils.InlineTimer("Seq", tinfo)
            For x = 0 To 100
                Dim t As New GetPersonInfoQueryHandler
                t.Query = q
                res = t.Result
                t.FillEmail()
                t.FillPerson()
                t.FillAddress()
            Next
        End Using

        Debug.Print(tinfo("Paralell").List.Sum().ToString) 'Took:548449 ticks
        Debug.Print(tinfo("Seq").List.Sum().ToString)      'Took:731012 ticks

    End Sub

There is of course some time overhead when doing this, but the overhead is eaten up when in my case 3 sub/functions is called. The timesavings will be greater the longer each sub takes.

I belive this approach is a way to abstract some of the difficulties of multithreading in some given scenarios. 

Any comments to this approach is very welcome.  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Authors

PEtter Ekrann

Norway Norway
No Biography provided

No Biography provided

Comments and Discussions

 
GeneralMy vote of 3 Pinmemberdmjm-h5-Sep-14 6:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 3 Sep 2014
Article Copyright 2014 by PEtter Ekrann, Martin Helgesen
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid