Multithreading in Acumatica

Hello everybody,

today I want to say a few words about my practice of usage Multithreading in Acumatica. I have a dedicated server which has 40 cores. And I have a need to read plenty of records from some other system. General schema is like this:

  1. Create some graph instance.
  2. Fill created graph instance data views with needed data
  3. At created graph instance call method graphInstance.Actions.PressSave();

And that schema is perfectly workable, but for my disappoitment my 40 cores were not busy at all with doing something useful. So I had to figure out how to use them more fully. 

Here are some fragments of code that I've used:

            int numberOfLogicalCores = Environment.ProcessorCount;
            List<Thread> threads = new List<Thread>(numberOfLogicalCores);
            int sizeOfOneChunk = (customers.Count / numberOfLogicalCores) + 1;
            for (int i = 0; i < numberOfLogicalCores; i++)
                int a = i;
                var thr = new Thread(
                    () =>
                        var portionsCustomers = customers.Skip(a * sizeOfOneChunk).Take(sizeOfOneChunk).ToList();
                thr.Name = $"thr{i}";
            foreach (var thread in threads)
            foreach (var thread in threads)

As you can see from presented code it doesn't do anything fancy. Get number of logical processors, for each logical processor creates Thread, each thread creates it's own graph instance ( in function InsertCustomersFromList), called method Actions.PressSave().

Also in graph I have the following syncrhonization object:

private Object thisLock = new Object();

Take a look at how lock is used:

            custMaint.Clear(); // This graph was created before
//some filling fields logic             custMaint.DefAddress.SetValueExt<Address.addressLine1>(custMaint.DefAddress.Current, customer.AddressLine1);             custMaint.DefAddress.SetValueExt<Address.addressLine2>(custMaint.DefAddress.Current, customer.AddressLine2);             custMaint.DefAddress.SetValueExt<>(custMaint.DefAddress.Current, customer.City);             custMaint.DefAddress.SetValueExt<Address.countryID>(custMaint.DefAddress.Current, customer.Country);             custMaint.DefAddress.SetValueExt<Address.state>(custMaint.DefAddress.Current, customer.StateCode);             custMaint.DefAddress.SetValueExt<Address.postalCode>(custMaint.DefAddress.Current, customer.PostalCode);             var subAccount = PXSelect<SubWhere<Sub.subCDEqual<Required<Sub.subCD>>>>                 .Select(custMaint, customer.SalesSubaccount.Replace("-""")).FirstOrDefault();             if (subAccount != null)             {                 var subAcTyped = subAccount.GetItem<Sub>();                 custMaint.DefLocation.SetValueExt<LocationExtAddress.cSalesSubID>(custMaint.DefLocation.Current,                     subAcTyped.SubID);             }             lock (thisLock)             {                 custMaint.Actions.PressSave();             }

And that's it with multithreading. In my tests I've got improvement in 3 times! In sigle threaded mode 110 recoreds went into Acumatica during 3 minutes, but with multithreading feature records went there in 1 minute. 

Add comment