Multithreaded parameter reading in ReadOnly

Multithreaded parameter reading in ReadOnly

ankofl
Advocate Advocate
405 Views
2 Replies
Message 1 of 3

Multithreaded parameter reading in ReadOnly

ankofl
Advocate
Advocate

@jeremytammik 
Everyone knows that the application of transactions is available only in one thread, but what about reading the parameters of an element from several threads?

Reading the geometry of an object from several streams into a string works fine (and it gives a multiple increase in execution speed), but if you get at least a list of parameters, not to mention reading them,

// in 121-124 line
else
{
    var param = elem.Parameters;
}


the revit simply closes even without an error report. 

namespace Client.Main
{
	[Transaction(TransactionMode.Manual)]
	public class MultiThread : IExternalCommand
	{

		public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
		{
			Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

			Document Doc = commandData.Application.ActiveUIDocument.Document;

			string data = "";
			try
			{
				Stopwatch timer = new Stopwatch();

				int threadCount = 5;

				var list = new FilteredElementCollector(Doc).OfClass(typeof(Wall)).WhereElementIsNotElementType().ToList();

				for (int i = 0; i < 10; i++)
				{
					list.AddRange(new FilteredElementCollector(Doc).OfClass(typeof(Wall)));
				}

				List<string> listResult = new List<string>();

				timer.Start();
				listResult = GetData(list);
				timer.Stop();
				data += $"Синхронно {timer.ElapsedMilliseconds}мс\t Count:{listResult.Count}\n";


				if (DivideList(list, threadCount, out List<List<Element>> listOut))
				{
					timer.Reset();
					timer.Start();
					listResult = new List<string>();

					List<Task<List<string>>> listTask = new List<Task<List<string>>>();

					for (int i = 0; i < listOut.Count; i++)
					{
						var listCur = listOut[i];

						var task = Task.Run(() => GetIdAsync(listCur));
						listTask.Add(task);
					}

					Task.WaitAll(listTask.ToArray());
					foreach (var task in listTask)
					{
						List<string> ids = task.Result;

						listResult.AddRange(ids);
					}
					timer.Stop();
					data += $"Многопоточно {timer.ElapsedMilliseconds}мс\t Count:{listResult.Count}\n";


					timer.Reset();
					timer.Start();
					List<string>[] array = new List<string>[listOut.Count];
					Parallel.For(0, listOut.Count, x =>
					{
						var listCur = listOut[x];
						var dataParallel = GetData(listCur);
						array[x] = dataParallel;
					});					
					for (int i = 0; i < array.Length; i++)
					{
						listResult.AddRange(array[i]);
					}
					timer.Stop();
					data += $"Параллельно {timer.ElapsedMilliseconds}мс\t Count:{listResult.Count}\n";

				}
			}
			catch (Exception e)
			{
				data = e.Message;
			}

			TaskDialog.Show("Result", data);
			
			return Result.Succeeded;
		}

		public static List<string> GetData(List<Element> listElem)
		{
			List<string> listData = new List<string>();
			foreach (var elem in listElem)
			{
				if (true)
				{
					var geom = elem.get_Geometry(new Options()); //.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);
					foreach (var geoElem in geom)
					{
						if (geoElem is Solid solid)
						{
							foreach (Face face in solid.Faces)
							{
								if (face is PlanarFace pFace)
								{
									Mesh mesh = pFace.Triangulate();
									for (int i = 0; i < mesh.NumTriangles; i++)
									{
										var tri = mesh.get_Triangle(i);
										XYZ a = tri.get_Vertex(0);
										XYZ b = tri.get_Vertex(1);
										XYZ c = tri.get_Vertex(2);

										listData.Add($"{a.X:f4} {a.Y:f4} {a.Z:f4} {b.X:f4} {b.Y:f4} {b.Z:f4} {c.X:f4} {c.Y:f4} {c.Z:f4} ");
									}
								}
							}
						}
					}
				}
				else
				{
					var param = elem.Parameters;
				}
			}
			return listData;
		}

		public static async Task<List<string>> GetIdAsync(List<Element> listElem)
		{
			return GetData(listElem);
		}

		public static bool DivideList(List<Element> listElem, int coountList, out List<List<Element>> listOut)
		{
			listOut = null;
			if (listElem == null || coountList <= 0 || listElem.Count < coountList)
			{
				return false;
			}

			int chunkSize = (int)Math.Ceiling((double)listElem.Count / coountList);
			listOut = Enumerable.Range(0, coountList).Select(i => listElem.Skip(i * chunkSize).Take(chunkSize).ToList()).ToList();
			return true;
		}
	}
}

 
If this is a known issue, can someone check if it is fixed in Revit 2025?

0 Likes
Accepted solutions (1)
406 Views
2 Replies
Replies (2)
Message 2 of 3

jeremy_tammik
Alumni
Alumni
Accepted solution

All use of the Revit API must be made in the one and only main thread provided by a valid Revit API context.

  

We have seen examples of multi-threaded operations working, sometimes, temporarily, for a while. Such multi-thread operations can also catastrophically corrupt the model and destroy the BIM.

  

It may be tempting to use multi-threaded read-only access. In some cases, the Revit API actively tries to prevent it. From the official point of view of the Revit API developers, it is completely forbidden.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 3 of 3

ankofl
Advocate
Advocate

Thanks for the prompt response!

0 Likes