LabVIEW Craft
  • Blog
  • About
  • Contact
  • Tools
  • Resources

Parallelism: C# vs LabVIEW

5/19/2016

6 Comments

 
Owen Search
In my current role I have the opportunity to use a variety of tools to solve the problems I am presented with at work.  While LabVIEW and TestStand were my key value propositions as a new hire, I quickly found myself gravitating toward C# as my tool of choice.  This is in no small part due to the clunky nature of interacting with ActiveX APIs in LabVIEW.
Picture
After spending some time programming in C# I have found a number of things about it that I really enjoy.  There are extraordinarily useful features of the .NET framework and C# itself that get me excited to tackle complex problems.  Some features of .NET/C# that I miss in LabVIEW include include generics, lambdas, LINQ and interfaces.
However, my experience in LabVIEW had taught me to approach problems in a way that began to challenge my ability to develop in C#.  Dataflow and parallelism as core elements of LabVIEW development had caused me to think of every problem as an opportunity for concurrent operations.  As I began writing more "traditional" code in C# I was immediately looking for ways to create parallel branches of execution and spawn asynchronous processes.  This can be daunting when first learning about concurrency in traditional text based languages, however my LabVIEW experience gave me the tools I needed to jump right in.
Text based languages are naturally suited toward procedural programming but concurrent programming can be a much more difficult proposition.  It is both difficult to reason about and to visualize concurrent software structure in a text based language.  LabVIEW solves this problem with it's graphical element; in my opinion this is one of the true powers of the language.  It makes concurrent programming accessible to even novice language users.
To explore the differences in concurrent programming in C# vs LabVIEW let's first look at the classic example of LabVIEW concurrency in action, the producer-consumer design pattern.
Picture
This simple architecture is one of the first a novice LabVIEW programmer will learn on the path to good LabVIEW coding practice.  Despite the simplicity of this diagram, the application structure is actually quite complex.  Few introductory programming courses for text based programming would have a novice building an application with this structure and for good reason.
Let's break it down.  The primary things to notice here are the following:
  1. The pattern relies on a queue
  2. There are two two entirely asynchronous loops at work
In most languages implementing this is non-trivial because you will need to use multi-threading to achieve asynchronous operation and hence worry about thread safety during implementation of the queue.  Granted, in LabVIEW thread safety is still a concern, but it is a much more intuitive concept to grasp when you look at program structure visually. 
Implementing this producer-consumer pattern C# is not a huge leap but it is certainly more difficult to visualize, and in my opinion, is therefore less accessible to a newbie programmer.  Let's take a look at the C# implementation.
namespace Example
{

    using System;
    using System.Collections.Generic;
    using System.Collections.Concurrent;
    using System.Threading;

    /// <summary>
    /// Application entrypoint
    /// </summary>
    public class ProducerConsumerMain
    {

        /// <summary>
        /// Here I create a concurrent queue for thread safe
        /// data comms between producer and consumer
        /// I create the consumer as a function running
        /// in a thread parallel to main
        /// The producer is simply a function that sends a number
        /// of messages, including the consumer stop signal
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {

            // Create a queue to pass messages between producer and consumer
            // We use a concurrent queue to account for thread safety issues
            ConcurrentQueue<TextMessage> queue 
                = new ConcurrentQueue<TextMessage>();

            // Create a thread to run our consumer in parallel to this 
            // thread (main)
            // I am using a lambda expression here to define the
            // thread's entrypoint and pass it the queue
            // This is because the default function signature for threads is 
            // static void <function_name>()
            // or static void <function_name>( object params )
            // Using a lambda makes it easy to pass the queue in directly
            Thread consumer = new Thread( _ => RunConsumer(queue) );
            consumer.Start();

            // Run the function acting as our producer
            RunProducer(queue);

            // The stop message sent by the producer
            // should cause the consumer loop to terminate
            // so we join on the finished thread
            // If the application hangs here
            // we know the consumer did not stop properly
            consumer.Join();

            // Wait for user to close the console
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();

        }

        /// <summary>
        /// This is our consumer loop, it will run until it
        /// gets a message to stop
        /// Otherwise it just prints the message it received
        /// </summary>
        static void RunConsumer(ConcurrentQueue<TextMessage> queue)
        {

            // Run until told to stop
            bool stop = false;

            // Loop on receiving data until we are told to stop
            // by the producer
            while( !stop )
            {
                // Create a new TextMessage to store data sent
                // by the producer
                TextMessage message;
                if(queue.TryDequeue(out message))
                {
                    // Here we would run our consumer logic
                    // For instance, we could use the object message to
                    // dispatch the execution of a particular state in a 
                    // state machine architecture
                    // Here we just see if it is a stop message
                    // if not we just print the message
                    if (message.MessageText == "Stop")
                    {
                        stop = true;
                        Console.WriteLine("Consumer shutting down");
                    } else
                    {
                        Console.WriteLine(message.MessageText);
                    }
                }
            }

        }

        /// <summary>
        /// This is our producer loop, it will just loop through a
        /// predefined list of messages to send the consumer
        /// The message list includes telling the consumer to stop
        /// </summary>
        /// <param name="queue"></param>
        static void RunProducer(ConcurrentQueue<TextMessage> queue)
        {

            // Define some messages to send to the consumer
            List<TextMessage> messages = new List<TextMessage>()
            {
                new TextMessage(),
                new TextMessage("I'm a message!"),
                new TextMessage("Goodbye Cruel World!"),
                new TextMessage("Stop")
            };

            // Loop through each message and send it to the consumer
            foreach( TextMessage message in messages )
            {
                queue.Enqueue(message);
            }

        }

    }

    /// <summary>
    /// The TextMessage class represents a simple data structure
    /// for passing string messages between producer and consumer
    /// </summary>
    public class TextMessage
    {

        /// <summary>
        /// Store the text of this TextMessage
        /// </summary>
        private string _messageText;
        public string MessageText
        {
            get
            {
                return _messageText;
            }
        }

        /// <summary>
        /// Create a new TextMessage with a default message
        /// </summary>
        public TextMessage()
        {
            _messageText = "Hello world!";
        }

        /// <summary>
        /// Create a new TextMessage with an arbitrary message
        /// </summary>
        /// <param name="messageText"></param>
        public TextMessage( string messageText )
        {
            _messageText = messageText;
        }

    }

}
As you can see, there are certainly more details for the programmer to be aware of in the C# example.  The downside of the LabVIEW approach is that working at that level of abstraction removes the flexibility to do more advanced things.  For instance you lose the choice of whether to use a simple queue versus a concurrent queue versus your own queue class.  You're kind of stuck with NI's implementation of the queue.
Moving from designing software in LabVIEW to doing so in C# has taught me that LabVIEW is an incredibly effective tool for teaching high-level software architecture concepts.  I came out of my development experience being able to immediately grasp the concepts of concurrent programming in C#.  That knowledge is also is much more accessible due to it's graphical nature.  I have gained an extensive understanding of software design patterns, system architectures, messaging strategies, FPGA and RT design considerations and many more software engineering concepts through LabVIEW.  The icing on the cake is that I was able to gain all that experience in just 3 years of my professional experience with the tool.

Owen Search is an Avionics Test Engineer at SpaceX.  He is a Certified LabVIEW Architect, Certified TestStand Architect, Certified LabVIEW Embedded Systems Developer, Certified LabWindows/CVI Developer and NI Certified Professional Instructor.  C# is his current language of choice but he has a soft spot for C/C++ and a love of LabVIEW.  He holds a BS in Biomedical Engineering from the University of Connecticut.
6 Comments
Bob Hamburger
5/19/2016 08:42:10 am

The longer I program in LabVIEW, the more I view parallel asynchronous programming as an (sometimes necessary) evil, to be used sparingly and only when a strong design case for it can be justified. I have encountered far too many applications in which the developers have jumped headlong into a maze of parallel loops with little thought of critical issues such as determinism, data integrity, race conditions, priority inversion, etc. Debugging a large application with many parallel processes is like being at a cocktail party and trying to follow a dozen conversations around you when everyone is talking at the same time.

Reply
Owen J Search
5/19/2016 11:51:03 am

All of those concerns are exactly the reason why concurrent programming in most languages is looked at something you should do sparingly. I think the general rule of thumb is that if you need stackoverflow to help debug your race condition, you probably shouldn't be threading. It's funny though, concurrency in LabVIEW is such a native concept of the language that the culture around it (in general) seems to send complete opposite message. In LabVIEW sometimes it seems concurrency is the hammer to every nail. After programming in C# for awhile and trying to solve every problem with a new thread, I have taken a step back and begun to look much more critically at using concurrent approaches to problem solving. It really took jumping back onto a text-based language to understand the magnitude of the problems that concurrency can cause. I think that may be one of the downsides of LabVIEW's high level of abstraction. As it makes things easier, it hides those parts of the process that may make a developer think twice before throwing down a parallel loop.

Reply
Jim Fowler
6/6/2016 12:28:42 pm

Nice post, Owen, and always nice to run into you in the community, Bob.

Years ago when first delving into OO in C++, I found the concepts daunting at first. Translating the concepts to LabVIEW OO later on in my career, while not trivial, seemed cake compared to taking it on in text based languages. From my first "fork" and "join" commands, thread safety also seemed pretty intimidating at the time, but now in LabVIEW it's very true that many of us throw around threads and loops with abandon as if the overhead and complexity doesn't much matter. (Jon M can attest to my thread happiness, I admit somewhat sheepishly.) NI are certainly adept at making multithreading and parallelism look easy, at least from the outside.

Having been thoroughly spoiled by LabVIEW development, it's nice to hear someone else make the observation, "Hey, LabVIEW makes a fantastic tool for teaching or learning some of these software engineering concepts." It seems as though most people I converse with in the community did not start out with text base languages.

You've inspired me to go back and apply what I've learned in LabVIEW, dust off my C++ books, and maybe even try my hand at C#. (It was probably inevitable!)


Reply
Toufiq Hossain
11/21/2016 07:56:40 pm

hi Owen Search.
It's Toufiq. I am trying to communicate with a Device which can be used as signal generator. the vendors have given me the .dll files for sharing the libraries from other softwares.
I am facing a problem regarding .Net Functions (such as constructor Node, Invoke Node) while I want to use these things in myRIO for accessing the .dll files created by C#. do you have any suggestion for me.

thank you in advance

Reply
Toufiq Hossain
11/21/2016 07:56:50 pm

hi Owen Search.
It's Toufiq. I am trying to communicate with a Device which can be used as signal generator. the vendors have given me the .dll files for sharing the libraries from other softwares.
I am facing a problem regarding .Net Functions (such as constructor Node, Invoke Node) while I want to use these things in myRIO for accessing the .dll files created by C#. do you have any suggestion for me.

thank you in advance

Reply
Mark Bidar link
11/6/2019 08:26:38 pm

I strongly suggest that you look into .NET TPL (Task Parallelism Library) and System.Threading.Tasks.Dataflow. It does provide you Dataflow, concurrencies, buffering and queue that you are looking for. In fact, I developed .NET application that receives telemetry data from 20 devices concurrencies and it performs quite well. The code is small and efficient.

Reply



Leave a Reply.

    RSS Feed

    Tags

    All
    Abstract Messaging
    Actor Framework
    Agile
    AI
    Asynchronous Programming
    Best Practices
    C#
    Complexity
    Continuations
    Control Values By Index
    Craftsmanship
    Dependency Inversion
    Dependency Viewer
    Futures/Promises
    Genetic Algorithm
    Liskov Substitution
    Malleable VIs
    Mediator Pattern
    .NET
    Object Oriented
    Object-Oriented
    Packed Project Library
    Parallelism
    Polymorphism
    Pub/Sub
    RawCap
    Root Loop
    Scrum
    Task Parallel Library
    TCP/IP
    TDD
    Test Engineering
    UML
    Unit Test
    VI Scripting
    VI Server
    WireShark

    Archives

    April 2019
    July 2018
    October 2017
    March 2017
    February 2017
    January 2017
    December 2016
    July 2016
    June 2016
    May 2016
    April 2016
    March 2016
    February 2016
    January 2016
    December 2015
    November 2015
    October 2015
    August 2015
    February 2015
    January 2015

    LabVIEW Blogs

    Eyes on VIs
    LabVIEW Hacker
    VI Shots
    LabVIEW Artisan
    Software Engineering for LabVIEW
    ​Wiresmith Techology
Picture
Powered by the Panda Ant

  • Blog
  • About
  • Contact
  • Tools
  • Resources