This is a very weird situation, first the code...
The code
private List<DispatchInvoiceCTNDataModel> WorksheetToDataTableForInvoiceCTN(ExcelWorksheet excelWorksheet, int month, int year)
{
int totalRows = excelWorksheet.Dimension.End.Row;
int totalCols = excelWorksheet.Dimension.End.Column;
DataTable dt = new DataTable(excelWorksheet.Name);
// for (int i = 1; i <= totalRows; i++)
Parallel.For(1, totalRows + 1, (i) =>
{
DataRow dr = null;
if (i > 1)
{
dr = dt.Rows.Add();
}
for (int j = 1; j <= totalCols; j++)
{
if (i == 1)
{
var colName = excelWorksheet.Cells[i, j].Value.ToString().Replace(" ", String.Empty);
lock (lockObject)
{
if (!dt.Columns.Contains(colName))
dt.Columns.Add(colName);
}
}
else
{
dr[j - 1] = excelWorksheet.Cells[i, j].Value != null ? excelWorksheet.Cells[i, j].Value.ToString() : null;
}
}
});
var excelDataModel = dt.ToList<DispatchInvoiceCTNDataModel>();
// now we have mapped everything expect for the IDs
excelDataModel = MapInvoiceCTNIDs(excelDataModel, month, year, excelWorksheet);
return excelDataModel;
}
The problem
When I am running the code on random occasion it would throw IndexOutOfRangeException on the line
dr[j - 1] = excelWorksheet.Cells[i, j].Value != null ? excelWorksheet.Cells[i, j].Value.ToString() : null;
For some random value of i and j. When I step over the code (F10), since it is running in a ParallelLoop, some other thread kicks and and other exception is throw, that other exception is something like (I could not reproduce it, it just came once, but I think it is also related to this threading issue) Column 31 not found in excelWorksheet. I don't understand how could any of these exception occur?
case 1
The IndexOutOfRangeException should not even occur, as the only code/shared variable dt I have locked around accessing it, rest all is either local or parameter so there should not have any thread related issue. Also, if I check the value of i or j in debug window, or even evaluate this whole expression dr[j - 1] = excelWorksheet.Cells[i, j].Value != null ? excelWorksheet.Cells[i, j].Value.ToString() : null; or a part of it in Debug window, then it works just fine, no errors of any sort or nothing.
case 2
For the second error, (which unfortunately is not reproducing now, but still) it should not have occurred as there are 33 columns in the excel.
More Code
In case someone might need how this method was called
using (var xlPackage = new ExcelPackage(viewModel.postedFile.InputStream))
{
ExcelWorksheets worksheets = xlPackage.Workbook.Worksheets;
// other stuff
var entities = this.WorksheetToDataTableForInvoiceCTN(worksheets[1], viewModel.Month, viewModel.Year);
// other stuff
}
Other
If someone needs more code/details let me know.
Update
Okay, to answer some comments. It is working fine when using for loop, I have tested that many times. Also, there is no particular value of i or j for which the exception is thrown. Sometimes it is 8, 6 at other time it could be anything, say 19,2or anything. Also, in the Parallel loop the +1 is not doing any damage as the msdn documentation says it is exclusive not inclusive. Also, if that were the issue I would only be getting exception at the last index (the last value of i) but that's not the case.
UPDATE 2
The given answer to lock around the code
dr = dt.Rows.Add();
I have changed it to
lock(lockObject) {
dr = dt.Rows.Add();
}
It is not working. Now I am getting ArgumentOutOfRangeException, still if I run this in debug window, it just works fine.
Update 3
Here is the full exception detail, after update 2 (I am getting this on the line that I mentioned in update 2)
System.ArgumentOutOfRangeException was unhandled by user code
HResult=-2146233086
Message=Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
Source=mscorlib
ParamName=index
StackTrace:
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at System.Collections.Generic.List`1.get_Item(Int32 index)
at System.Data.RecordManager.NewRecordBase()
at System.Data.DataTable.NewRecordFromArray(Object[] value)
at System.Data.DataRowCollection.Add(Object[] values)
at AdminEntity.BAL.Service.ExcelImportServices.<>c__DisplayClass2e.<WorksheetToDataTableForInvoiceCTN>b__2d(Int32 i) in C:\Projects\Manager\Admin\AdminEntity\AdminEntity.BAL\Service\ExcelImportServices.cs:line 578
at System.Threading.Tasks.Parallel.<>c__DisplayClassf`1.<ForWorker>b__c()
InnerException:
Okay. So there are a few problems with your existing code, most of which have been touched on by others:
Parallel threads are at the mercy of the OS scheduler; therefore, although threads are queued in-order, they may (and often do) complete execution out-of-order. For example, given Parallel.For(0, 10, (i) => { Console.WriteLine(i); });, the first four threads (on a quad-core system) will be queued with i values 0-3. But any one of those threads may start or finish executing before any other. So you may see 2 printed first, whereupon thread 4 will be queued. Then thread 1 might complete, and thread 5 will be queued. Then thread 4 might complete, even before threads 0 or 3 do. Etc., etc. TL;DR: You CANNOT assume an ordered output in parallel.
Given that, as #ScottChamberlain noted, it's a very bad idea to do column generation within your parallel loop - because you have no guarantee that the thread doing column generation will create all your columns before another thread starts assigning data in rows to those column indices. E.g. you could be assigning data to cell [0,4] before your table actually has a fifth column.
It's worth noting that this should really be broken out of the loop anyway, purely from a code cleanliness perspective. At the moment, you have two nested loops, each with special behavior on a single iteration; better to separate that setup logic into its own loop and leave the main loop to assign data and nothing else.
For the same reason, you should not be creating new rows in the table within your parallel loop - because you have no guarantee that the rows will be added to the table in their source order. Break that out too, and access rows within the loop based on their index.
Some have mentioned using DataRow.NewRow() before Rows.Add(). Technically, NewRow() is the right way to go about things, but the actual recommended access pattern is a bit different than is probably appropriate for a cell-by-cell function, particularly when parallelism is intended (see MSDN: DataTable.NewRow Method). The fact remains that adding a new, blank row to a DataTable with Rows.Add() and populating it afterwards functions properly.
You can clean up your string formatting with the null-coalescing operator ??, which evaluates whether the preceding value is null, and if so, assigns the subsequent value. For example, foo = bar ?? "" is the equivalent of if (bar == null) { foo = ""; } else { foo = bar; }.
So right off the bat, your code should look more like this:
private void ReadIntoTable(ExcelWorksheet sheet)
{
DataTable dt = new DataTable(sheet.Name);
int height = sheet.Dimension.Rows;
int width = sheet.Dimension.Columns;
for (int j = 1; j <= width; j++)
{
string colText = (sheet.Cells[1, j].Value ?? "").ToString();
dt.Columns.Add(colText);
}
for (int i = 2; i <= height; i++)
{
dt.Rows.Add();
}
Parallel.For(1, height, (i) =>
{
var row = dt.Rows[i - 1];
for (int j = 0; j < width; j++)
{
string str = (sheet.Cells[i + 1, j + 1].Value ?? "").ToString();
row[j] = str;
}
});
// convert to your special Excel data model
// ...
}
Much better!
...but it still doesn't work!
Yep, it still fails with an IndexOutOfRange exception. However, since we took your original line dr[j - 1] = excelWorksheet.Cells[i, j].Value != null ? excelWorksheet.Cells[i, j].Value.ToString() : null; and split it into a couple pieces, we can see exactly which part it fails on. And it fails on row[j] = str;, where we actually write the text into the row.
Uh-oh.
MSDN: DataRow Class
Thread Safety
This type is safe for multithreaded read operations. You must synchronize any write operations.
*sigh*. Yeah. Who knows why DataRow uses static anything when assigning values, but there you have it; writing to DataRow isn't thread-safe. And sure enough, doing this...
private static object s_lockObject = "";
private void ReadIntoTable(ExcelWorksheet sheet)
{
// ...
lock (s_lockObject)
{
row[j] = str;
}
// ...
}
...magically makes it work. Granted, it completely destroys the parallelism, but it works.
Well, it almost completely destroys the parallelism. Anecdotal experimentation on an Excel file with 18 columns and 46319 rows shows that the Parallel.For() loop creates its DataTable in about 3.2s on average, whereas replacing Parallel.For() with for (int i = 1; i < height; i++) takes about 3.5s. My guess is that, since the lock is only there for writing data, there is a very small benefit realized by writing data on one thread and processing text on the other(s).
Of course, if you can create your own DataTable replacement class, you can see a much larger speed boost. For example:
string[,] rows = new string[height, width];
Parallel.For(1, height, (i) =>
{
for (int j = 0; j < width; j++)
{
rows[i - 1, j] = (sheet.Cells[i + 1, j + 1].Value ?? "").ToString();
}
});
This executes in about 1.8s on average for the same Excel table mentioned above - about half the time of our barely-parallel DataTable. Replacing the Parallel.For() with the standard for() in this snippet makes it run in about 2.5s.
So you can see a significant performance boost from parallelism, but also from a custom data structure - although the viability of the latter will depend on your ability to easily convert the returned values to that Excel data model thing, whatever it is.
The line dr = dt.Rows.Add(); is not thread safe, you are corrupting the internal state of the array in the DataTable that hold the rows for the table.
At first glance changing it to
if (i > 1)
{
lock (lockObject)
{
dr = dt.Rows.Add();
}
}
should fix it, but that does not mean other thread safety problems are not there from excelWorksheet.Cells being accessed from multiple threads. (If excelWorksheet is this class and you are running a STA main thread (WinForms or WPF) COM should marshal the cross thread calls for you)
EDIT: New thory, the problem comes from the fact that you are setting up your schema inside the parallel loop and attempting to write to it at the same time. Pull out all of the i == 1 logic to before the loop and then start at i == 2
private List<DispatchInvoiceCTNDataModel> WorksheetToDataTableForInvoiceCTN(ExcelWorksheet excelWorksheet, int month, int year)
{
int totalRows = excelWorksheet.Dimension.End.Row;
int totalCols = excelWorksheet.Dimension.End.Column;
DataTable dt = new DataTable(excelWorksheet.Name);
//Build the schema before we loop in parallel.
for (int j = 1; j <= totalCols; j++)
{
var colName = excelWorksheet.Cells[1, j].Value.ToString().Replace(" ", String.Empty);
if (!dt.Columns.Contains(colName))
dt.Columns.Add(colName);
}
Parallel.For(2, totalRows + 1, (i) =>
{
DataRow dr = null;
lock(lockObject) {
dr = dt.Rows.Add();
}
for (int j = 1; j <= totalCols; j++)
{
dr[j - 1] = excelWorksheet.Cells[i, j].Value != null ? excelWorksheet.Cells[i, j].Value.ToString() : null;
}
});
var excelDataModel = dt.ToList<DispatchInvoiceCTNDataModel>();
// now we have mapped everything expect for the IDs
excelDataModel = MapInvoiceCTNIDs(excelDataModel, month, year, excelWorksheet);
return excelDataModel;
}
You code is incorrect:
1) Parallel.For has its own batching mechanism (can be customized with ForEach with partitioners though) and does not guarantee that operation with (for) i==n will be executed after operation with i==m where n>m.
So line
dr[j - 1] = excelWorksheet.Cells[i, j].Value != null ? excelWorksheet.Cells[i, j].Value.ToString() : null;
throw exception when required column is not added yet (in {i==1} operation}
2) And it's recommended to use NewRow method:
dr=tbl.NewRow->Populate dr->tbl.Rows.Add(dr)
or Rows.Add(object[] values):
values=[KnownColumnCount]->Populate values->tbl.Rows.Add(values)
3) It's really better to populate columns first in this case, because it's sequential access to excel file (seek) and It would not harm perfomance
Have you tried using NewRow when creating the new datarow and moving the creation of the columns outside the parallel loop like Scott Chamberlain suggested above? By using newrow you're creating a row with the same schema as the parent datatable. I got the same error as you when I tried your code with a random excel file, but got it to work like this:
for (int x = 1; x <= totalCols; x++)
{
var colName = excelWorksheet.Cells[1, x].Value.ToString().Replace(" ", String.Empty);
if (!dt.Columns.Contains(colName))
dt.Columns.Add(colName);
}
Parallel.For(2, totalRows + 1, (i) =>
{
DataRow dr = null;
for (int j = 1; j <= totalCols; j++)
{
dr = dt.NewRow();
dr[j - 1] = excelWorksheet.Cells[i, j].Value != null
? excelWorksheet.Cells[i, j].Value.ToString()
: null;
lock (lockObject)
{
dt.Rows.Add(dr);
}
}
});
Related
I'm working on a text-based Tetris game and running into some issues with clearing lines. I have my DeleteRow() method which deletes the given row by working upwards from the given row, overwriting each row's data with the data of the row above it. This seems to work:
/**
* Deletes row r from the array of landed Tetromino blocks.
* #param r The row to delete from the landed array.
**/
private void DeleteRow(int row) {
for(int r = row; r > 0; r--) {
for(int c = 0; c < Board.WIDTH; c++) {
// Overwrite the value of this column from the row above.
still[r, c] = still[(r - 1), c];
}
}
}
Where "still" is the 2D array defined as such.
private int[,] still;
And initialized here:
public Board() {
still = new int[Board.HEIGHT, Board.WIDTH];
}
But in my CheckRows() method, which is what calls DeleteRow(), I seem to be having an issue where it will clear the first row that's passed to it, but subsequent rows are either ignored or it will delete the wrong row:
/**
* Checks each row to see if they are full, and if so, deletes the row and adds to score.
* #param score A long representing the current score, passed as reference.
**/
public void CheckRows(ref long score) {
List<int> rowsToClear = new List<int>();
for(int r = 0; r < Board.HEIGHT; r++) {
bool zero = false;
for(int c = 0; c < Board.WIDTH; c++) {
if(still[r, c] == 0) {
zero = true;
break;
}
}
if(!zero) rowsToClear.Add(r);
}
// Delete all the rows that did not contain zeros.
if(rowsToClear.Count > 0) {
// Add to the score depending on the number of rows cleared.
score += (new int[4] { 40, 100, 300, 1200 })[rowsToClear.Count - 1];
// Delete each row in the list and then increment all other row indices to account for
// the fact that the rows above this one are being copied to this one.
for(int i = (rowsToClear.Count - 1); i >= 0; i--) {
DeleteRow(rowsToClear[i]);
rowsToClear.ForEach(x => x++);
}
}
}
I have to assume this has something to do with the line following the call to DeleteRow, where I increment the row numbers of the other rows that need to be cleared to account for the fact that I'm shifting each row downward to delete the row.
I have noticed though that if those rows are not deleted in the first CheckRows() call, they will be in the next iteration of the main game loop.
Is there some flaw in the logic I'm using? This is my first time making a Tetris game. I'm doing this to familiarize myself with C#, and I'm just not sure what the issue is here. Can someone else spot what's wrong?
I have to assume this has something to do with the line following the call to DeleteRow, where I increment the row numbers of the other rows that need to be cleared to account for the fact that I'm shifting each row downward to delete the row.
This is the line you are speaking of:
rowsToClear.ForEach(x => x++);
That line of code does absolutely nothing: The values will not be incremented.
You can perform an action on the element passed to the delegate in ForEach but you cannot replace it with a new value. Here is the documentation from MSDN:
Modifying the underlying collection in the body of the Action delegate is not supported and causes undefined behavior.
That will not work and neither will this:
foreach (var thisNum in nums)
{
thisNum++; // <== will not compile
}
This will increment the list:
for (int i = nums.Count - 1; i >= 0; i--)
{
nums[i]++;
}
<== Try Me ==>
After rearranging my CheckRows method around, I realized that the solution was that I needed to delete the rows in top to bottom order, as opposed to bottom to top.
I have this simple function,working as a task, that only print the values of a dataset. I pass the dataset from main function, and the index. The problem is that i have populated only 2 dataset index, however the function always jumps one ahead, i.e. in the last iteration it would want to start reading index 2, which is uninitialized and therefore the exception.
for (int i = 0; i < 2; i++)
{
tasks.Add(Task.Factory.StartNew(() => {
int a = i;
showNodeID(dataSet,a);
}));
}
and the function is
private static void showNodeID(DataSet[] ds, int a)
{
Console.WriteLine(a.ToString());
Console.WriteLine(ds[a].GetXml());
} //END
In the last iteration when i print 1 however in function if i print a it would be 2.
I assume you are aware of the dangers of captured counter variables in lambda closures, since you attempt to avoid the issue by assigning the counter to a locally-scoped variable. However, your assignment is happening too late – by the time the task starts and copies the value, the counter might already have been incremented by the next iteration. To properly avoid the issue, you need to copy the value before the task, not within it:
for (int i = 0; i < 2; i++)
{
int a = i;
tasks.Add(Task.Factory.StartNew(() =>
{
showNodeID(dataSet, a);
}));
}
If you just need to perform a parallel loop, you could alternatively use:
Parallel.For(0, 2, i => showNodeID(dataSet, i));
I'm writing a chess engine in C#, and I'm trying to debug move generation. I've been using breakpoints so far to check variables, but I need a better way to debug. Despite the fact that a breakpoint shows that this list has a length of 86, the loop only runs 5 times. The only thing I can think of is that the arrays in the list have a length of 5, but I have no idea what would be causing this.
foreach (int[] debugBoard in futures)
{
for (int i = 0; i < 5; i++)
{
Debug.Write(debugBoard[i].ToString().PadLeft(3, ' '));
}
Debug.Write("\n");
int[,] debugOutBoard = new int[8, 8];
Array.Copy(chessBoard.Pieces(), debugOutBoard, 64);
if (debugBoard[0] < 0 || debugBoard[1] < 0 || debugBoard[2] < 0 || debugBoard[3] < 0 || debugBoard[0] > 7 || debugBoard[1] > 7 || debugBoard[2] > 7 || debugBoard[3] > 7)
break;
debugOutBoard[debugBoard[2], debugBoard[3]] = debugOutBoard[debugBoard[0], debugBoard[1]];
debugOutBoard[debugBoard[0], debugBoard[1]] = 0;
int rowLength = debugOutBoard.GetLength(0);
int colLength = debugOutBoard.GetLength(1);
for (int i = 0; i < rowLength; i++)
{
for (int j = 0; j < colLength; j++)
{
Debug.Write(debugOutBoard[i, j].ToString().PadLeft(3, ' '));
}
Debug.Write(Environment.NewLine + Environment.NewLine);
}
}
Also, I tried using a concurrentbag to store moves in (I was going to multithread the move processing later on) but as soon as the foreach loop touched the concurrent bag, all the memory values of the bag changed to one value. I've been stuck on this roadblock for days, and I really need help.
Your if statement breaks out of the for loop when its condition is met, I presume this happens for the first time on the 5th/6th iteration.
What I meant to do is iterate to the next element. What command would I use to do that? –
You need to use continue instead of break
If 'futures' contains 86 items the only way it could stop iterating is if an exception occurs. Visual studio (In default settings) should break when this occurs unless you handle the exception somewhere.
Wrap the whole thing in a try{} catch{} and set a breakpoint in catch and see if it hits.
Take a look at the program below. It's pretty self-explanatory, but I'll explain anyway :)
I have two methods, one fast and one slow. These methods do the exact same thing: they create a table with 50,000 rows and 1000 columns. I write to a variable number of columns in the table. In the code below I've picked 10 (NUM_COLS_TO_WRITE_TO).
In other words, only 10 columns out of the 1000 will actually contain data. OK. The only difference between the two methods is that the fast populates the columns and then calls DataTable.AddRow, whereas the slow one does it after. That's it.
The performance difference however is shocking (to me anyway). The fast version is almost completely unaffected by changing the number of columns we write to, whereas the slow one goes up linearly. For example, when the number of columns I write to is 20, the fast version takes 2.8 seconds, but the slow version takes over a minute.
What in the world could possibly be going on here?
I thought that maybe adding dt.BeginLoadData would make a difference, and it did to some extent, it brought the time down from 61 seconds to ~50 seconds, but that's still a huge difference.
Of course, the obvious answer is, "Well, don't do it that way." OK. Sure. But what in world is causing this? Is this expected behavior? I sure didn't expect it. :)
public class Program
{
private const int NUM_ROWS = 50000;
private const int NUM_COLS_TO_WRITE_TO = 10;
private const int NUM_COLS_TO_CREATE = 1000;
private static void AddRowFast() {
DataTable dt = new DataTable();
//add a table with 1000 columns
for (int i = 0; i < NUM_COLS_TO_CREATE; i++) {
dt.Columns.Add("x" + i, typeof(string));
}
for (int i = 0; i < NUM_ROWS; i++) {
var theRow = dt.NewRow();
for (int j = 0; j < NUM_COLS_TO_WRITE_TO; j++) {
theRow[j] = "whatever";
}
//add the row *after* populating it
dt.Rows.Add(theRow);
}
}
private static void AddRowSlow() {
DataTable dt = new DataTable();
//add a table with 1000 columns
for (int i = 0; i < NUM_COLS_TO_CREATE; i++) {
dt.Columns.Add("x" + i, typeof(string));
}
for (int i = 0; i < NUM_ROWS; i++) {
var theRow = dt.NewRow();
//add the row *before* populating it
dt.Rows.Add(theRow);
for (int j=0; j< NUM_COLS_TO_WRITE_TO; j++){
theRow[j] = "whatever";
}
}
}
static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
AddRowFast();
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
sw.Restart();
AddRowSlow();
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
//When NUM_COLS is 5
//FAST: 2754.6782
//SLOW: 15794.1378
//When NUM_COLS is 10
//FAST: 2777.431 ms
//SLOW 32004.7203 ms
//When NUM_COLS is 20
//FAST: 2831.1733 ms
//SLOW: 61246.2243 ms
}
}
Update
Calling theRow.BeginEdit and theRow.EndEdit in the slow version makes the slow version more or less constant (~4 seconds on my machine). If I actually had some constraints on the table, I guess this might make sense to me.
When attached to table, much more work is being done to record and track the state on every change.
For example, if you do this,
theRow.BeginEdit();
for (int j = 0; j < NUM_COLS_TO_WRITE_TO; j++)
{
theRow[j] = "whatever";
}
theRow.CancelEdit();
Then in BeginEdit() , internally it's taking a copy of the contents of the row, so that at any point, you can rollback - and the end result of the above is an empty row again without whatever. This is still possible, even when in BeginLoadData mode. Following the path ofBeginEdit if attached to a DataTable, eventually you get into DataTable.NewRecord() which shows that it is just copying each value for every column to store the original state incase a cancel is needed - not much magic here. On the other hand, if not attached to a datatable, not much is happening in BeginEdit at all and it is exited quickly.
EndEdit() is similarly pretty heavy (when attached), as here all the constraints etc are checked (max length, does column allow nulls etc). Also it fires a bunch of events, explictly frees storage used incase the edit was cancelled, and makes available for recall with DataTable.GetChanges(), which is still possible in BeginLoadData. Infact looking at source all BeginLoadData seems to do is turn off constraint checking and indexing.
So that describes what BeginEdit and EditEdit does, and they are completely different when attached or not attached in terms of what is stored. Now consider that a single theRow[j] = "whatever" you can see on the indexer setter for DataRow it calls BeginEditInternal and then EditEdit on every single call (if it is not already in an edit because you explicitly called BeginEdit earlier). So that means it's copying and storing every single value for each column in the row, every time you do this call. So you're doing it 10 times, that means with your 1,000 column DataTable, over 50,000 rows, it means it is allocating 500,000,000 objects. On top of this there is all the other versioning, checks and events being fired after every single change, and so, overall, it's much slower when the row is attached to a DataTable than when not.
I've recently optimized my function which insert some values to excel workbook with Parallel For loop(i have to compare about 500000 values in the loop). If i use simple for loop everything works good for me, but if i start to use Parallel For statement, i have no mistakes and code works fine, but values are inserted not as expected in excel workbook (in different rows, not like when i use simple for loop).
This is my code sample, can you help me please, to find a solution.
Parallel.For(0, DestinationListIDArray.Count, (int i) =>
{
for (int j = 0; j < SourceListIDArray.Count; j++)
{
if (DestinationListIDArray[i].ToString() == SourceListIDArray[j].ToString() && DestinationListIDArray[i].ToString() != "НД")
{
int c = 1;
int summ = i + c;
string forB = summ.ToString();
string forC = summ.ToString();
DestrangeH = myExcelWorksheetDestination.get_Range(TEXTBOX_FIO_DESTINATION.Text + forB);
DestrangeI = myExcelWorksheetDestination.get_Range(TEXTBOX_DEST_DOLZHNOST.Text + forC);
DestrangeH.Interior.Color = System.Drawing.ColorTranslator.ToOle(COLORDIALOG_INF1_BACKGROUNDCOLOR.Color);
DestrangeH.Font.Color = System.Drawing.ColorTranslator.ToOle(COLORDIALOG_INF1_FOREGROUNDCOLOR.Color);
DestrangeI.Interior.Color = System.Drawing.ColorTranslator.ToOle(COLORDIALOG_INF2_BACKGROUNDCOLOR.Color);
DestrangeI.Font.Color = System.Drawing.ColorTranslator.ToOle(COLORDIALOG_INF2_FOREGROUNDCOLOR.Color);
//DestrangeH.set_Value(Missing.Value, SourceArray[j - 2].ToString());
//DestrangeI.set_Value(Missing.Value, SourceArray[j - 1].ToString());
DestrangeH.set_Value(Missing.Value, SourceListFIOArray[j].ToString());
DestrangeI.set_Value(Missing.Value, SourceListDolzhArray[j].ToString());
}
}
});
I am using a List arrays and i read somewhere that List generics is not thread safe, may be it makes me in trouble. If so can you advice me please what kind of dinamic array to use and how to lock before insert. thanks..
Just as I recalled. Read here: http://msdn.microsoft.com/en-us/library/8sesy69e.aspx
TL;DR
Office object model is not thread-safe. You can use multiple threads in certain scenarios, however the COM server serializes the calls for you.
You can't gain anything from using Paraller.For here.