pclarke7
asked on
Background Worker Thread and Global Variables
I have a WCF Service written in C# which makes extensive user of global variables. The Service runs menus and transaction which are made up of sequenced database records with each database record capable of calling specific logic based on inbuilt commands. A typical simplistic example could be:
05 Start
10 Display Screen
20 Input Range of Items
30 run SQL statement to retrieve item details from Item master table
40 Display Item Details
50 Confirm selected Items
*60 Run SQL statement to report on Item Usage
*70 Output SQL results to CSV
*80 Email usage report to user
90 Goto Start
Those marked with * should run in a background thread so that after seq 50 the user will be returned to seq 05 whilst the background thread takes over. The issue I have is that Many of the variables controlling the transactions are global variables and so when the user returns to 05 and then exists to a new transactions this effects the background job since the background job is running the same code as the UI , except in the background thread.
I have copied all of the variables from UI to the background thread however the UI continues to update these global background thread variables after the background thread is launched. How can I stop the UI from affecting the background thread global variables ?
Pat
05 Start
10 Display Screen
20 Input Range of Items
30 run SQL statement to retrieve item details from Item master table
40 Display Item Details
50 Confirm selected Items
*60 Run SQL statement to report on Item Usage
*70 Output SQL results to CSV
*80 Email usage report to user
90 Goto Start
Those marked with * should run in a background thread so that after seq 50 the user will be returned to seq 05 whilst the background thread takes over. The issue I have is that Many of the variables controlling the transactions are global variables and so when the user returns to 05 and then exists to a new transactions this effects the background job since the background job is running the same code as the UI , except in the background thread.
I have copied all of the variables from UI to the background thread however the UI continues to update these global background thread variables after the background thread is launched. How can I stop the UI from affecting the background thread global variables ?
Pat
To be blunt change the design and don't use global variables, it is bad practice.
Hi pclarke7;
I am in total agreement with @Andy but with that said if I had to complete this project in its current state this is what I would do.
At this point the changes made to the global namespace will not effect the BackgroundWorker because it has its own values.
I am in total agreement with @Andy but with that said if I had to complete this project in its current state this is what I would do.
- Create a class that has the variables that are in the global namespace that will be used in the BackgroundWorker
- Create and instance of that class and copy over the variables from global namespace if a variable is a ref type you will need to create a new object for that variable and not copy it
- When you call the BackgroundWorker pass in this object as a parameter
- In the BackgroundWorker use the values from the object you just passed in.
At this point the changes made to the global namespace will not effect the BackgroundWorker because it has its own values.
ASKER
Hi Andy & Fernando,
Point 1 - Use of Globals
I'm relatively new to c# and so interested to find out why it's considered bad practice to use global variables. I have used them extensively in this project and my reasons for doing so (rightly or wrongly) was to make the classes & variables available to the various .cs files within the project. Without using globals I believe that I would have to copy each of the classes and variables to each .cs file.
below is the list of globals within this project
I am open to any advice you can give me for V2.
Point 2 - Completing the Project
Fernando, the UI logic references all of the above global variables. As you can see there are dictionaries, Lists, Classes etc.. (g.MyVarValuesDict, g.MyTransSeqList, g.MyUser etc..) What is the best way of copying these objects by value ?
Are you suggesting that I create a globalsB class (identical to globals) which would hold all of the copied (by value) values in globals and then pass globalsB into the background worker as a parameter. Will I still be able to refer to these globalB values as g.MyVarValuesDict, g.MyTransSeqList, g.MyUser etc.. ? Would you have a simple example of doing this ?
regards
Pat
Point 1 - Use of Globals
I'm relatively new to c# and so interested to find out why it's considered bad practice to use global variables. I have used them extensively in this project and my reasons for doing so (rightly or wrongly) was to make the classes & variables available to the various .cs files within the project. Without using globals I believe that I would have to copy each of the classes and variables to each .cs file.
below is the list of globals within this project
public class globals
{
public globals g;
public TransactionService MyTransactionService;
public UserMaster MyUser;
public RegDetails MyRegDetails;
public EnterpriseMaster MyEnterprise;
public TransactionHdr MyTransHdr;
public TransactionSeq MyTransSeq;
public TransactionsWaitingToLoad MyTransWaiting;
public ExecuteTrans MyExe;
public Commands MyCommands;
public ErrorMsgs MyErrMsgs;
public PromptMsgs MyPromptMsgs;
public ConnectionStrings MyConnString;
public Environments MyEnvironments;
public RuleTypeOpSeq MyRuleTypeOpSeq;
public LogMessages MyLogMessages;
public DataBaseIO MyDataBaseIO;
public ErrorsEncountered MyErrorsEncountered;
public DisplaySeq MyDisplaySeq;
public PassToServiceParms MyPassToServiceParms;
public RtnFromServiceParms MyRtnFromServiceParms;
public InputValues MyInpFldVal;
public Object MyVarObj;
public NextNumbers MyNextNumbers;
public TransOutput MyTransOutput;
public Invitation MyInvitation;
public AppDistLst MyAppDistLst;
public List<TransactionHdr> MyTransHdrList;
public List<TransactionSeq> MyTransSeqList;
public List<Environments> MyEnvironmentList;
public List<ErrorMsgs> MyErrMsgsList;
public List<PromptMsgs> MyPromptMsgsList;
public List<ConnectionStrings> MyConnStringList;
public List<DisplaySeq> MyDisplaySeqList;
public List<TransactionsWaitingToLoad> MyTransWaitingList;
public List<ErrorsEncountered> MyErrorsEncounteredList ;
public List<InputValues> MyInpFldValList ;
public Dictionary<string, int> MyTagsDict;
public Dictionary<string, int> MyCharDict;
public Dictionary<string, string> MyVarValuesDict;
public Dictionary<string, string> MyVarTypesDict;
public Dictionary<string, string> MyCondLvlsDict;
public Dictionary<string, string> MyRuleTypeOpSeqDict;
public Dictionary<string, string> MyLogMessagesDict;
public Dictionary<string, string> MyKeywordsDict;
public Dictionary<string, int[]> MyPrevSelDict;
public Dictionary<string, object> MyObjDict ;
public Dictionary<string, string> MyAppDistLstDict;
public Dictionary<string, string> MyNextNumbersDict;
public TransLvlValues MyTransLvlValues;
public SessionLvlValues MySessionLvlValues;
public AppLvlValues MyAppLvlValues;
// Create global objects
//
public TransactionService CrtTransactionService()
{
MyTransactionService = new TransactionService();
return MyTransactionService;
}
public UserMaster CrtUserMaster()
{
MyUser = new UserMaster();
return MyUser;
}
public RegDetails CrtRegDetails()
{
MyRegDetails = new RegDetails();
return MyRegDetails;
}
public EnterpriseMaster CrtEnterpriseMaster()
{
MyEnterprise = new EnterpriseMaster();
return MyEnterprise;
}
public TransactionHdr CrtTransactionHdr()
{
MyTransHdr = new TransactionHdr();
return MyTransHdr;
}
public TransactionSeq CrtTransactionSeq()
{
MyTransSeq = new TransactionSeq();
return MyTransSeq;
}
public TransactionsWaitingToLoad CrtTransactionsWaitingToLoad()
{
MyTransWaiting = new TransactionsWaitingToLoad();
return MyTransWaiting;
}
public ExecuteTrans CrtExecuteTrans()
{
MyExe = new ExecuteTrans();
return MyExe;
}
public Commands CrtCommands()
{
MyCommands = new Commands();
return MyCommands;
}
public ErrorMsgs CrtErrorMsgs()
{
MyErrMsgs = new ErrorMsgs();
return MyErrMsgs;
}
public PromptMsgs CrtPromptMsgs()
{
MyPromptMsgs = new PromptMsgs();
return MyPromptMsgs;
}
public ConnectionStrings CrtConnectionStrings()
{
MyConnString = new ConnectionStrings();
return MyConnString;
}
public Environments CrtEnvironments()
{
MyEnvironments = new Environments();
return MyEnvironments;
}
public RuleTypeOpSeq CrtRuleTypeOpSeq()
{
MyRuleTypeOpSeq = new RuleTypeOpSeq();
return MyRuleTypeOpSeq;
}
public LogMessages CrtLogMessages()
{
MyLogMessages = new LogMessages();
return MyLogMessages;
}
public DataBaseIO CrtDataBaseIO()
{
MyDataBaseIO = new DataBaseIO();
return MyDataBaseIO;
}
public ErrorsEncountered CrtErrorsEncountered()
{
MyErrorsEncountered = new ErrorsEncountered();
return MyErrorsEncountered;
}
public DisplaySeq CrtDisplaySeq()
{
MyDisplaySeq = new DisplaySeq();
return MyDisplaySeq;
}
public PassToServiceParms CrtPassToServiceParms()
{
MyPassToServiceParms = new PassToServiceParms();
return MyPassToServiceParms;
}
public RtnFromServiceParms CrtRtnFromServiceParms()
{
MyRtnFromServiceParms = new RtnFromServiceParms();
return MyRtnFromServiceParms;
}
public InputValues CrtInputValues()
{
MyInpFldVal = new InputValues();
return MyInpFldVal;
}
public Object CrtVarObj()
{
MyVarObj = new Object();
return MyVarObj;
}
public NextNumbers CrtNextNumbers()
{
MyNextNumbers = new NextNumbers();
return MyNextNumbers;
}
public AppDistLst CrtAppDistLst()
{
MyAppDistLst = new AppDistLst();
return MyAppDistLst;
}
public TransLvlValues CrtTransLvlValues()
{
MyTransLvlValues = new TransLvlValues();
return MyTransLvlValues;
}
public SessionLvlValues CrtSessionLvlValues()
{
MySessionLvlValues = new SessionLvlValues();
return MySessionLvlValues;
}
public AppLvlValues CrtAppLvlValues()
{
MyAppLvlValues = new AppLvlValues();
return MyAppLvlValues;
}
public TransOutput CrtTransOutput()
{
MyTransOutput = new TransOutput();
return MyTransOutput;
}
public Invitation CrtInvitation()
{
MyInvitation = new Invitation();
return MyInvitation;
}
//*****************************
// Create new List Objects
//*****************************
public List<TransactionHdr> CrtTransHdrList()
{
MyTransHdrList = new List<TransactionHdr>();
return MyTransHdrList;
}
public List<TransactionSeq> CrtTransSeqList()
{
MyTransSeqList = new List<TransactionSeq>();
return MyTransSeqList;
}
public List<Environments> CrtEnvironmentList()
{
MyEnvironmentList = new List<Environments>();
return MyEnvironmentList;
}
public List<ErrorMsgs> CrtErrMsgsList()
{
MyErrMsgsList = new List<ErrorMsgs>();
return MyErrMsgsList;
}
public List<PromptMsgs> CrtPromptMsgsList()
{
MyPromptMsgsList = new List<PromptMsgs>();
return MyPromptMsgsList;
}
public List<ConnectionStrings> CrtConnStringList()
{
MyConnStringList = new List<ConnectionStrings>();
return MyConnStringList;
}
public List<DisplaySeq> CrtDisplaySeqList()
{
MyDisplaySeqList = new List<DisplaySeq>();
return MyDisplaySeqList;
}
public List<TransactionsWaitingToLoad> CrtTransWaitingList()
{
MyTransWaitingList = new List<TransactionsWaitingToLoad>();
return MyTransWaitingList;
}
public List<ErrorsEncountered> CrtErrorsEncounteredList()
{
MyErrorsEncounteredList = new List<ErrorsEncountered>();
return MyErrorsEncounteredList;
}
public List<InputValues> CrtInpFldValList()
{
MyInpFldValList = new List<InputValues>();
return MyInpFldValList;
}
//*****************************
// Create new Dictionary Objects
//*****************************
public Dictionary<string, int> CrtTagsDict()
{
MyTagsDict = new Dictionary<string, int>();
return MyTagsDict;
}
public Dictionary<string, int> CrtCharDict()
{
MyCharDict = new Dictionary<string, int>();
return MyCharDict;
}
public Dictionary<string, string> CrtVarValuesDict()
{
MyVarValuesDict = new Dictionary<string, string>();
return MyVarValuesDict;
}
public Dictionary<string, string> CrtVarTypesDict()
{
MyVarTypesDict = new Dictionary<string, string>();
return MyVarTypesDict;
}
public Dictionary<string, string> CrtCondLvlsDict()
{
MyCondLvlsDict = new Dictionary<string, string>();
return MyCondLvlsDict;
}
public Dictionary<string, string> CrtRuleTypeOpSeqDict()
{
MyRuleTypeOpSeqDict = new Dictionary<string, string>();
return MyRuleTypeOpSeqDict;
}
public Dictionary<string, string> CrtLogMessagesDict()
{
MyLogMessagesDict = new Dictionary<string, string>();
return MyLogMessagesDict;
}
public Dictionary<string, string> CrtKeywordsDict()
{
MyKeywordsDict = new Dictionary<string, string>();
return MyKeywordsDict;
}
public Dictionary<string, int[]> CrtPrevSelDict()
{
MyPrevSelDict = new Dictionary<string, int[]>();
return MyPrevSelDict;
}
public Dictionary<string, object> CrtObjDict()
{
MyObjDict = new Dictionary<string, Object>();
return MyObjDict;
}
public Dictionary<string, string> CrtAppDistLstDict()
{
MyAppDistLstDict = new Dictionary<string, string>();
return MyAppDistLstDict;
}
public Dictionary<string, string> CrtNextNumbersDict()
{
MyNextNumbersDict = new Dictionary<string, string>();
return MyNextNumbersDict;
}
I am open to any advice you can give me for V2.
Point 2 - Completing the Project
Fernando, the UI logic references all of the above global variables. As you can see there are dictionaries, Lists, Classes etc.. (g.MyVarValuesDict, g.MyTransSeqList, g.MyUser etc..) What is the best way of copying these objects by value ?
Are you suggesting that I create a globalsB class (identical to globals) which would hold all of the copied (by value) values in globals and then pass globalsB into the background worker as a parameter. Will I still be able to refer to these globalB values as g.MyVarValuesDict, g.MyTransSeqList, g.MyUser etc.. ? Would you have a simple example of doing this ?
regards
Pat
>>Without using globals I believe that I would have to copy each of the classes and variables to each .cs file.
Yes, but there is nothing wrong with that. In fact it has big advantages, here are a couple.
In general keep variables as close to where you need them as possible and don't let other parts of your code access them if there is no reason for them to need accessing. In fact just creating one big class to hold all the previously global variables might not be a good idea. eg. Put database things together and just let those bits of your app requiring database access use them and not other bits that for instance perform custom drawing of a window.
Yes, but there is nothing wrong with that. In fact it has big advantages, here are a couple.
- It can simplify debugging, it is much easier to track down what is changing a value of one of the variables
- It makes multi threading much simpler to code - see this question
In general keep variables as close to where you need them as possible and don't let other parts of your code access them if there is no reason for them to need accessing. In fact just creating one big class to hold all the previously global variables might not be a good idea. eg. Put database things together and just let those bits of your app requiring database access use them and not other bits that for instance perform custom drawing of a window.
ASKER
Thanks Andy,
did you forget to add a link "see this question" ?
did you forget to add a link "see this question" ?
Hi pclarke7;
Please see this article from Wikipedia on the good and bad of Global variables some of which reposted below.
Please see this article from Wikipedia on the good and bad of Global variables some of which reposted below.
They are usually considered bad practice precisely because of their non-locality: a global variable can potentially be modified from anywhere (unless they reside in protected memory or are otherwise rendered read-only), and any part of the program may depend on it.[1] A global variable therefore has an unlimited potential for creating mutual dependencies, and adding mutual dependencies increases complexity. See action at a distance. Global variables also make it difficult to integrate modules because software written by others may use the same global names unless names are reserved by agreement, or by naming convention. However, in a few cases, global variables can be suitable for use. For example, they can be used to avoid having to pass frequently-used variables continuously throughout several functions. In practice, large programs may well require a large number of global variables because there are so many parameters that are shared between different functions, and care must be taken to make sure different functions share the global data without mishap.
Global variables are used extensively to pass information between sections of code that do not share a caller/callee relation like concurrent threads and signal handlers. Languages (including C) where each file defines an implicit namespace eliminate most of the problems seen with languages with a global namespace though some problems may persist without proper encapsulation. Without proper locking (such as with a mutex), code using global variables will not be thread-safe except for read only values in protected memory.
>>did you forget to add a link "see this question" ?
No, this question as in this question you have asked here and I am commenting about. It is an example of making life difficult for yourself by using global variables.
No, this question as in this question you have asked here and I am commenting about. It is an example of making life difficult for yourself by using global variables.
ASKER
Hi Fernando,
I am trying to use your suggested work around but still finding that the global variables in the background thread are being updated by the UI.
1.Create a class that has the variables that are in the global namespace that will be used in the BackgroundWorker
2.Create and instance of that class and copy over the variables from global namespace if a variable is a ref type you will need to create a new object for that variable and not copy it
Below I create a new globalsb which is identical to globals and will hold the global variables for the background job. Then I clone everything in globals g to globalsb gb.
3.When you call the BackgroundWorker pass in this object as a parameter
I then pass in gb as a parameter to the backgroundWorker:
In DoWork I do the following:
1. receive in backgroundWorker global variables gb
2. Create new global variables g
3. copy gb to g
However my UI is still updating the background worker global variables. Appreciate if you can cast you eye over what I am doing to see if you can spot where I am going wrong
regards
Pat
I am trying to use your suggested work around but still finding that the global variables in the background thread are being updated by the UI.
1.Create a class that has the variables that are in the global namespace that will be used in the BackgroundWorker
2.Create and instance of that class and copy over the variables from global namespace if a variable is a ref type you will need to create a new object for that variable and not copy it
Below I create a new globalsb which is identical to globals and will hold the global variables for the background job. Then I clone everything in globals g to globalsb gb.
public void StartBackgroundProcess()
{
try
{
if (MyGlobals.logger == null)
{
MyGlobals.logger = new LoggerConfiguration()
.ReadFrom.AppSettings()
.CreateLogger();
Log.Logger = MyGlobals.logger;
}
MyGlobals.logger.Information("STARTING BGJ: { BackgroundCancelError} User: {UserName} Enterprise: {Enterprise} Source: {Source} Transaction:{Transaction} Seq:{Seq} CatchNo: {CatchNo} ErrMsgId: {ErrMsgId} Error Description:{ErrDescr} Error Data:{ErrData} ", "STARTING>>>", g.MyExe.userId, g.MyExe.enterpriseId, g.MyExe.thisSourceMethod, g.MyExe.thisTransId, g.MyExe.curSeq, g.MyExe.catchNo, g.MyExe.errorMsg, g.MyExe.errorDescription, " ");
}
catch (Exception ex)
{
MyGlobals.logger.Error("STARTING BGJ2: { BackgroundCancelError}", ex.Message.ToString());
}
bg_Worker = new BackgroundWorker();
bg_Worker.DoWork += new DoWorkEventHandler(bg_Worker_DoWork);
bg_Worker.ProgressChanged += new ProgressChangedEventHandler
(bg_Worker_ProgressChanged);
bg_Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_Worker_RunWorkerCompleted);
bg_Worker.WorkerReportsProgress = true;
bg_Worker.WorkerSupportsCancellation = true;
globalsB gb = new globalsB(); // Background globalsb (identical to globals)
for (int i = 1; i <= 50; i++)
{
switch (i)
{
case 1: // TransactionService
gb.MyTransactionService = (TransactionService)g.MyTransactionService.Clone();
break;
case 2: // User
gb.MyUser = (UserMaster)g.MyUser.Clone();
break;
case 3: // MyRegDetails
gb.MyRegDetails= (RegDetails) g.MyRegDetails.Clone();
break;
case 4: // MyEnterprise
gb.MyEnterprise= (EnterpriseMaster) g.MyEnterprise.Clone();
break;
case 5: // MyDataBaseIO
gb.MyDataBaseIO=(DataBaseIO) g.MyDataBaseIO.Clone();
break;
case 6: // MyExe
gb.MyExe=(ExecuteTrans) g.MyExe.Clone();
break;
case 7: // MyErrMsgs
gb.MyErrMsgs = (ErrorMsgs) g.MyErrMsgs.Clone();
break;
case 8: // MyPromptMsgs
gb.MyPromptMsgs=(PromptMsgs)g.MyPromptMsgs.Clone();
break;
case 9: // MyConnString
gb.MyConnString= (ConnectionStrings) g.MyConnString.Clone();
break;
case 10: // MyLogMessages
gb.MyLogMessages=(LogMessages) g.MyLogMessages.Clone();
break;
case 11: // MyRuleTypeOpSeq
gb.MyRuleTypeOpSeq=(RuleTypeOpSeq) g.MyRuleTypeOpSeq.Clone();
break;
case 12: // MyErrMsgsList
gb.MyErrMsgsList= g.MyErrMsgsList.CloneObject();
break;
case 13: // MyEnvironments
gb.MyEnvironments=(Environments) g.MyEnvironments.Clone();
break;
case 14: // MyPromptMsgsList
gb.MyPromptMsgsList=g.MyPromptMsgsList.CloneObject();
break;
case 15: // MyConnStringList
gb.MyConnStringList=g.MyConnStringList.CloneObject();
break;
case 16: // MyEnvironmentList
gb.MyEnvironmentList=g.MyEnvironmentList.CloneObject();
break;
case 17: // MyLogMessagesDict
gb.MyLogMessagesDict=g.MyLogMessagesDict.CloneObject();
break;
case 18: // MyRuleTypeOpSeqDict
gb.MyRuleTypeOpSeqDict=g.MyRuleTypeOpSeqDict.CloneObject();
break;
case 19: // MyKeywordsDict
gb.MyKeywordsDict=g.MyKeywordsDict.CloneObject();
break;
case 20: // MyCommands
gb.MyCommands=(Commands) g.MyCommands.Clone();
break;
case 21: // MyObjDict
gb.MyObjDict=g.MyObjDict.CloneObject();
break;
case 22: // MyNextNumbers
gb.MyNextNumbers=(NextNumbers) g.MyNextNumbers.Clone();
break;
case 23: // MyAppDistLstDict
gb.MyAppDistLstDict=g.MyAppDistLstDict.CloneObject();
break;
case 24: // MyNextNumbersDict
gb.MyNextNumbersDict=g.MyNextNumbersDict.CloneObject();
break;
case 25: // MyInvitation
gb.MyInvitation=(Invitation) g.MyInvitation.Clone();
break;
case 26: // MyBGWorker
gb.MyBGWorker=(BGWorker) g.MyBGWorker.Clone();
break;
case 27: // MyTransHdr
gb.MyTransHdr=(TransactionHdr) g.MyTransHdr.Clone();
break;
case 28: // MyTransSeq
gb.MyTransSeq=(TransactionSeq) g.MyTransSeq.Clone();
break;
case 29: // MyTransWaiting
gb.MyTransWaiting= (TransactionsWaitingToLoad) g.MyTransWaiting.Clone();
break;
case 30: // MyErrorsEncountered
gb.MyErrorsEncountered=(ErrorsEncountered) g.MyErrorsEncountered.Clone();
break;
case 31: // MyDisplaySeq
gb.MyDisplaySeq=(DisplaySeq) g.MyDisplaySeq.Clone();
break;
case 32: // MyPassToServiceParms
gb.MyPassToServiceParms=(PassToServiceParms) g.MyPassToServiceParms.Clone();
break;
case 33: // MyRtnFromServiceParms
gb.MyRtnFromServiceParms=(RtnFromServiceParms) g.MyRtnFromServiceParms.Clone();
break;
case 34: // MyInpFldVal
gb.MyInpFldVal=(InputValues) g.MyInpFldVal.Clone();
break;
case 35: // MyVarObj
gb.MyVarObj=g.MyVarObj.CloneObject();
break;
case 36: // MyTransOutput
gb.MyTransOutput=(TransOutput) g.MyTransOutput.Clone();
break;
case 37: // MyTransHdrList
gb.MyTransHdrList=g.MyTransHdrList.CloneObject();
break;
case 38: // MyTransSeqList
gb.MyTransSeqList=g.MyTransSeqList.CloneObject();
break;
case 39: // MyDisplaySeqList
gb.MyDisplaySeqList=g.MyDisplaySeqList.CloneObject();
break;
case 40: // MyTransWaitingList
gb.MyTransWaitingList= g.MyTransWaitingList.CloneObject();
break;
case 41: // MyErrorsEncounteredList
gb.MyErrorsEncounteredList= g.MyErrorsEncounteredList.CloneObject();
break;
case 42: // MyInpFldValList
gb.MyInpFldValList=g.MyInpFldValList.CloneObject();
break;
case 43: // MyTagsDict
gb.MyTagsDict=g.MyTagsDict.CloneObject();
break;
case 44: // MyVarValuesDict
gb.MyVarValuesDict=g.MyVarValuesDict.CloneObject();
break;
case 45: // MyVarTypesDict
gb.MyVarTypesDict=g.MyVarTypesDict.CloneObject();
break;
case 46: // MyCondLvlsDict
gb.MyCondLvlsDict=g.MyCondLvlsDict.CloneObject();
break;
case 47: // MyPrevSelDict
gb.MyPrevSelDict=g.MyPrevSelDict.CloneObject();
break;
case 48: // Constant
gb.MyCharDict=g.MyCharDict.CloneObject();
break;
case 49: // MyTransLvlValues
gb.MyTransLvlValues=(TransLvlValues) g.MyTransLvlValues.Clone();
break;
case 50: // MySessionLvlValues
gb.MySessionLvlValues=(SessionLvlValues) g.MySessionLvlValues.Clone();
break;
default:
break;
}
}
//
// Start Background job
//
bg_Worker.RunWorkerAsync(gb); // Call backgroundWorker with list of jobs to run
}
3.When you call the BackgroundWorker pass in this object as a parameter
I then pass in gb as a parameter to the backgroundWorker:
bg_Worker.RunWorkerAsync(gb); // Call backgroundWorker with list of jobs to run
4.In the BackgroundWorker use the values from the object you just passed in.In DoWork I do the following:
1. receive in backgroundWorker global variables gb
2. Create new global variables g
3. copy gb to g
void bg_Worker_DoWork(object sender, DoWorkEventArgs e)
{
if(Thread.CurrentThread.Name==null)
{
Thread.CurrentThread.Name = "TiosBackgroundWorkerThread";
}
//
// retrieve the passed parameter globalsB and convert to globals
//
globalsB gb = e.Argument as globalsB;
globals g = new globals(); // Globals
// Create new global variables
g.MyTransactionService = g.CrtTransactionService();
g.MyUser = g.CrtUserMaster();
g.MyRegDetails = g.CrtRegDetails();
g.MyEnterprise = g.CrtEnterpriseMaster();
g.MyDataBaseIO = g.CrtDataBaseIO();
g.MyExe = g.CrtExecuteTrans();
g.MyErrMsgs = g.CrtErrorMsgs();
g.MyPromptMsgs = g.CrtPromptMsgs();
g.MyConnString = g.CrtConnectionStrings();
g.MyLogMessages = g.CrtLogMessages();
g.MyRuleTypeOpSeq = g.CrtRuleTypeOpSeq();
g.MyErrMsgsList = g.CrtErrMsgsList();
g.MyEnvironments = g.CrtEnvironments();
g.MyPromptMsgsList = g.CrtPromptMsgsList();
g.MyConnStringList = g.CrtConnStringList();
g.MyEnvironmentList = g.CrtEnvironmentList();
g.MyLogMessagesDict = g.CrtLogMessagesDict();
g.MyRuleTypeOpSeqDict = g.CrtRuleTypeOpSeqDict();
g.MyKeywordsDict = g.CrtKeywordsDict();
g.MyCommands = g.CrtCommands();
g.MyObjDict = g.CrtObjDict();
g.MyNextNumbers = g.CrtNextNumbers();
g.MyAppDistLstDict = g.CrtAppDistLstDict();
g.MyNextNumbersDict = g.CrtNextNumbersDict();
g.MyInvitation = g.CrtInvitation();
g.MyBGWorker = g.CrtBGWorker();
g.MyTransHdr = g.CrtTransactionHdr();
g.MyTransSeq = g.CrtTransactionSeq();
g.MyTransWaiting = g.CrtTransactionsWaitingToLoad();
g.MyErrorsEncountered = g.CrtErrorsEncountered();
g.MyDisplaySeq = g.CrtDisplaySeq();
g.MyPassToServiceParms = g.CrtPassToServiceParms();
g.MyRtnFromServiceParms = g.CrtRtnFromServiceParms();
g.MyInpFldVal = g.CrtInputValues();
g.MyVarObj = g.CrtVarObj();
g.MyTransOutput = g.CrtTransOutput();
g.MyTransHdrList = g.CrtTransHdrList();
g.MyTransSeqList = g.CrtTransSeqList();
g.MyDisplaySeqList = g.CrtDisplaySeqList();
g.MyTransWaitingList = g.CrtTransWaitingList();
g.MyErrorsEncounteredList = g.CrtErrorsEncounteredList();
g.MyInpFldValList = g.CrtInpFldValList();
g.MyTagsDict = g.CrtTagsDict();
g.MyVarValuesDict = g.CrtVarValuesDict();
g.MyVarTypesDict = g.CrtVarTypesDict();
g.MyCondLvlsDict = g.CrtCondLvlsDict();
g.MyPrevSelDict = g.CrtPrevSelDict();
g.MyCharDict = g.CrtCharDict();
g.MyTransLvlValues = g.CrtTransLvlValues();
g.MySessionLvlValues = g.CrtSessionLvlValues();
// Copy gb to g
g.MyTransactionService = gb.MyTransactionService;
g.MyUser = gb.MyUser;
g.MyRegDetails = gb.MyRegDetails;
g.MyEnterprise = gb.MyEnterprise;
g.MyDataBaseIO = gb.MyDataBaseIO;
g.MyExe = gb.MyExe;
g.MyErrMsgs = gb.MyErrMsgs;
g.MyPromptMsgs = gb.MyPromptMsgs;
g.MyConnString = gb.MyConnString;
g.MyLogMessages = gb.MyLogMessages;
g.MyRuleTypeOpSeq = gb.MyRuleTypeOpSeq;
g.MyErrMsgsList = gb.MyErrMsgsList;
g.MyEnvironments = gb.MyEnvironments;
g.MyPromptMsgsList = gb.MyPromptMsgsList;
g.MyConnStringList = gb.MyConnStringList;
g.MyEnvironmentList = gb.MyEnvironmentList;
g.MyLogMessagesDict = gb.MyLogMessagesDict;
g.MyRuleTypeOpSeqDict = gb.MyRuleTypeOpSeqDict;
g.MyKeywordsDict = gb.MyKeywordsDict;
g.MyCommands = gb.MyCommands;
g.MyObjDict = gb.MyObjDict;
g.MyNextNumbers = gb.MyNextNumbers;
g.MyAppDistLstDict = gb.MyAppDistLstDict;
g.MyNextNumbersDict = gb.MyNextNumbersDict;
g.MyInvitation = gb.MyInvitation;
g.MyBGWorker = gb.MyBGWorker;
g.MyTransHdr = gb.MyTransHdr;
g.MyTransSeq = gb.MyTransSeq;
g.MyTransWaiting = gb.MyTransWaiting;
g.MyErrorsEncountered = gb.MyErrorsEncountered;
g.MyDisplaySeq = gb.MyDisplaySeq;
g.MyPassToServiceParms = gb.MyPassToServiceParms;
g.MyRtnFromServiceParms = gb.MyRtnFromServiceParms;
g.MyInpFldVal = gb.MyInpFldVal;
g.MyVarObj = gb.MyVarObj;
g.MyTransOutput = gb.MyTransOutput;
g.MyTransHdrList = gb.MyTransHdrList;
g.MyTransSeqList = gb.MyTransSeqList;
g.MyDisplaySeqList = gb.MyDisplaySeqList;
g.MyTransWaitingList = gb.MyTransWaitingList;
g.MyErrorsEncounteredList = gb.MyErrorsEncounteredList;
g.MyInpFldValList = gb.MyInpFldValList;
g.MyTagsDict = gb.MyTagsDict;
g.MyVarValuesDict = gb.MyVarValuesDict;
g.MyVarTypesDict = gb.MyVarTypesDict;
g.MyCondLvlsDict = gb.MyCondLvlsDict;
g.MyPrevSelDict = gb.MyPrevSelDict;
g.MyCharDict = gb.MyCharDict;
g.MyTransLvlValues = gb.MyTransLvlValues;
g.MySessionLvlValues = gb.MySessionLvlValues;
// Receive background Global variables into various .cs file
g.MyDataBaseIO.ReceiveMyGlob(g);
g.MyExe.ReceiveMyGlob(g);
g.MyCommands.ReceiveMyGlob(g);
g.MyTransactionService.ReceiveMyGlob(g);
//
// Read through the transaction list in sequence but only process those transaction sequences which have a keyword
// of run-AsBGJ (Run as backgrouynd job)
//
for (int idx = 0; idx < g.MyTransSeqList.Count; idx++) // Loop through List with for loop
{
g.MyTransSeq = g.MyTransSeqList[idx];
if (g.MyTransSeq.keyword.ToLower().Contains("run-asbgj"))
{
//
// process transaction seq if it contains "run-asbgj"
//
g.MyExe.curSeq = idx;
BGWTransId = g.MyTransSeq.thisTransId;
g.MyExe.promptId = g.MyTransSeq.promptMsg;
MyGlobals.logger.Information("Transaction: {Trans} - Seq: {Seq} SeqListCount: {cnt1} Iteration: {iter} Thread:{thread}", g.MyTransSeq.thisTransId, g.MyTransSeq.seqNo, g.MyTransSeqList.Count().ToString(), idx.ToString(), "BackgroundThread XX-BEFORE-XX");
g.MyExe = g.MyTransactionService.ExecuteTransaction2(g.MyTransSeq, idx);
MyGlobals.logger.Information("Transaction: {Trans} - Seq: {Seq} SeqListCount: {cnt1} Iteration: {iter} Thread:{thread}", g.MyTransSeq.thisTransId, g.MyTransSeq.seqNo, g.MyTransSeqList.Count().ToString(), idx.ToString(), "BackgroundThread XX -AFTER-XX");
//
// This is where the seq is overridden by conditions and tags
//
if (g.MyExe.nextBackgroundSeq != -99)
{
idx = g.MyExe.nextBackgroundSeq;
g.MyExe.nextBackgroundSeq = -99;
}
}
}
However my UI is still updating the background worker global variables. Appreciate if you can cast you eye over what I am doing to see if you can spot where I am going wrong
regards
Pat
Hi Pat;
The reason why your modified code did not work was that you copied reference types to the new globals object. Reference types are an address to memory and not an actual value so copying it only copies the address which means that both the old and the new object are pointing to the same old object and not what you needed, two independent objects.
1 : Create a class that has the variables that are in the global namespace that will be used in the BackgroundWorker
2 : Create and instance of that class and copy over the variables from global namespace if a variable is a ref type you will need to create a new object for that variable and not copy it
3 : When you call the BackgroundWorker pass in this object as a parameter
4 : In the BackgroundWorker use the values from the object you just passed in.
The reason why your modified code did not work was that you copied reference types to the new globals object. Reference types are an address to memory and not an actual value so copying it only copies the address which means that both the old and the new object are pointing to the same old object and not what you needed, two independent objects.
1 : Create a class that has the variables that are in the global namespace that will be used in the BackgroundWorker
In your case you have a class which is globals so you don't have to crate a new class you already have globals all you would need to do is mark the class Serializable shown here
[Serializable()]
public class Globals
All properties/variables in this class which themselves are classes, that class must also be marked Serializable as shown above.
2 : Create and instance of that class and copy over the variables from global namespace if a variable is a ref type you will need to create a new object for that variable and not copy it
This part is not so simple because your class does not implement a Deepcopy which is need to copy over basic variable types as well as create NEW reference types for the reference variables. The below function will do a DeepCopy as long as all the class objects in the globals is not a sealed class and the class has been marked Serializable
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
private Globals DeepCopyGlobas(globals original)
{
//Opens a memory stream and serializes the object into it in binary format.
Stream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, original);
// Reset the stream to the begining and deserializes the object
stream.Position = 0;
formatter = new BinaryFormatter();
// Rehydrate the global variables into a new object
globals newGlobals = (Globals)formatter.Deserialize(stream);
stream.Close();
return newGlobals;
}
3 : When you call the BackgroundWorker pass in this object as a parameter
bg_Worker.RunWorkerAsync(gb);
// Where gb was the return value from DeepCopy
4 : In the BackgroundWorker use the values from the object you just passed in.
void bg_Worker_DoWork(object sender, DoWorkEventArgs e)
{
if(Thread.CurrentThread.Name==null)
{
Thread.CurrentThread.Name = "TiosBackgroundWorkerThread";
}
//
// retrieve the passed parameter globals. Globals were already copied before entering BGW
//
globals g = e.Argument as globals;
// Receive background Global variables into various .cs file
g.MyDataBaseIO.ReceiveMyGlob(g);
g.MyExe.ReceiveMyGlob(g);
g.MyCommands.ReceiveMyGlob(g);
g.MyTransactionService.ReceiveMyGlob(g);
//
// Read through the transaction list in sequence but only process those transaction sequences which have a keyword
// of run-AsBGJ (Run as backgrouynd job)
//
for (int idx = 0; idx < g.MyTransSeqList.Count; idx++) // Loop through List with for loop
{
g.MyTransSeq = g.MyTransSeqList[idx];
if (g.MyTransSeq.keyword.ToLower().Contains("run-asbgj"))
{
//
// process transaction seq if it contains "run-asbgj"
//
g.MyExe.curSeq = idx;
BGWTransId = g.MyTransSeq.thisTransId;
g.MyExe.promptId = g.MyTransSeq.promptMsg;
MyGlobals.logger.Information("Transaction: {Trans} - Seq: {Seq} SeqListCount: {cnt1} Iteration: {iter} Thread:{thread}", g.MyTransSeq.thisTransId, g.MyTransSeq.seqNo, g.MyTransSeqList.Count().ToString(), idx.ToString(), "BackgroundThread XX-BEFORE-XX");
g.MyExe = g.MyTransactionService.ExecuteTransaction2(g.MyTransSeq, idx);
MyGlobals.logger.Information("Transaction: {Trans} - Seq: {Seq} SeqListCount: {cnt1} Iteration: {iter} Thread:{thread}", g.MyTransSeq.thisTransId, g.MyTransSeq.seqNo, g.MyTransSeqList.Count().ToString(), idx.ToString(), "BackgroundThread XX -AFTER-XX");
//
// This is where the seq is overridden by conditions and tags
//
if (g.MyExe.nextBackgroundSeq != -99)
{
idx = g.MyExe.nextBackgroundSeq;
g.MyExe.nextBackgroundSeq = -99;
}
}
}
I hope that made it a bit clearer.
ASKER
Hi Fernando,
thanks for your detailed response. I have followed your instructions but still finding that my UI objects are updating my backgroundworker objects. I have performed a deep copy of each class within globals g to globalsB gb and even used Object.ReferenceEquals to ensure that false is returned for each object cloned. I then pass in globalsB gb as the parameter to DoWork in backgroundworker.
In DoWork,as soon as I receive the parameter globalsB gb I re-create each class belonging to globals g and then copy the equivalent gb classes to the g classes. I should now be in a position to call the methods that I would usually call via UI , via the backgroundworker thread.
This is what happens.
scenario 1 - when background worker job is submitted it begins to process transactions correctly as expected. However if, before the background job finishes, I go into another transaction via UI then the background job crashes. On examination on the classes I can see that some of the primary Lists used by the background worker (which should be static for the duration of the thread) has changed and now contain the values of the new UI transaction. It's as though they were looking at the same objects rather than separate objects.
scenario 2 - when background worker job is submitted it begins to process transactions correctly as expected. If I allow the job to complete before I maneuver to another transaction then the background job completes successfully.
Question
Lets say I have a common function Function1() which is called by UI and also by backgroundworker. This function iterates through a list called g.TransSeqList from start to finish. When running Transaction A the application does the following:
1. Populates list g.TransSeqList from database from transaction A
2. takes a deep copy of g.TransSeqList to gb.TransSeqList
3. Calls backgroundworker passing gb.TransSeqList as parameter
4. in backgroundworker gb.TransSeqList is copied to g.TransSeqList (shallow copy)
5. backgroundworker calls function Function1() which will iterate through g.TransSeqList (could take 5/10 minutes to complete)
Whilst 5 above is processing the user has control back of UI and goes to a new transaction Transaction B. Transaction B does not use any backgroundworker but instead processes everything using the UI. It does the following:
1. Populates list g.TransSeqList from database from Transaction B
2.UI calls function Function1() which will iterate through g.TransSeqList (This and 5 above will be running at the same time, one in UI and one in backgroundworker)
I am assuming that this logic is ok and the the value of g.TransSeqList in UI (Transaction B) will not conflict or overwrite the value of g.TransSeqList in backgroundworker. Am I right in this assumption ?
regards
Pat
thanks for your detailed response. I have followed your instructions but still finding that my UI objects are updating my backgroundworker objects. I have performed a deep copy of each class within globals g to globalsB gb and even used Object.ReferenceEquals to ensure that false is returned for each object cloned. I then pass in globalsB gb as the parameter to DoWork in backgroundworker.
In DoWork,as soon as I receive the parameter globalsB gb I re-create each class belonging to globals g and then copy the equivalent gb classes to the g classes. I should now be in a position to call the methods that I would usually call via UI , via the backgroundworker thread.
This is what happens.
scenario 1 - when background worker job is submitted it begins to process transactions correctly as expected. However if, before the background job finishes, I go into another transaction via UI then the background job crashes. On examination on the classes I can see that some of the primary Lists used by the background worker (which should be static for the duration of the thread) has changed and now contain the values of the new UI transaction. It's as though they were looking at the same objects rather than separate objects.
scenario 2 - when background worker job is submitted it begins to process transactions correctly as expected. If I allow the job to complete before I maneuver to another transaction then the background job completes successfully.
Question
Lets say I have a common function Function1() which is called by UI and also by backgroundworker. This function iterates through a list called g.TransSeqList from start to finish. When running Transaction A the application does the following:
1. Populates list g.TransSeqList from database from transaction A
2. takes a deep copy of g.TransSeqList to gb.TransSeqList
3. Calls backgroundworker passing gb.TransSeqList as parameter
4. in backgroundworker gb.TransSeqList is copied to g.TransSeqList (shallow copy)
5. backgroundworker calls function Function1() which will iterate through g.TransSeqList (could take 5/10 minutes to complete)
Whilst 5 above is processing the user has control back of UI and goes to a new transaction Transaction B. Transaction B does not use any backgroundworker but instead processes everything using the UI. It does the following:
1. Populates list g.TransSeqList from database from Transaction B
2.UI calls function Function1() which will iterate through g.TransSeqList (This and 5 above will be running at the same time, one in UI and one in backgroundworker)
I am assuming that this logic is ok and the the value of g.TransSeqList in UI (Transaction B) will not conflict or overwrite the value of g.TransSeqList in backgroundworker. Am I right in this assumption ?
regards
Pat
Hi Pat;
I will try to answer your questions but not knowing the layout of the program structure it is difficult. One thing you need to remember is that when you are executing the BackgroundWorker you should be using globalsB gb and NOT g, when NOT executing BackgroundWorker you should be using g.
You state this,
To your statement
I will try to answer your questions but not knowing the layout of the program structure it is difficult. One thing you need to remember is that when you are executing the BackgroundWorker you should be using globalsB gb and NOT g, when NOT executing BackgroundWorker you should be using g.
You state this,
In DoWork,as soon as I receive the parameter globalsB gb I re-create each class belonging to globals g and then copy the equivalent gb classes to the g classes. I should now be in a position to call the methods that I would usually call via UI , via the backgroundworker thread.Once you did a DeepCopy which you are calling globalsB gb you have all the parameters you need for the DoWork function you should NOT be doing what I have underlined above. By doing that you are undoing the DeepCopy.
To your statement
Lets say I have a common function Function1() which is called by UI and also by backgroundworker. This function iterates through a list called g.TransSeqList from start to finish. When running Transaction A the application does the following:When you call it from your BackgroundWorker you should be using the DeepCopy / gb copy and NOT g.
ASKER
Hi Fernando,
ok - maybe this is where the problem lies. I am indeed copying globals g to globalsB gb and then in the backgroundworker copying globalsB.gb back to globals g. I was under the assumption that globals g in background thread would be different to globals g in the UI.
The reason why I was doing this is because the logic that the background worker needs to run is identical to that of the UI logic. I have thousands of lines of code referring to g.xxxxxxx and I did not want to have to change logic
From:
To:
regards
Pat
ok - maybe this is where the problem lies. I am indeed copying globals g to globalsB gb and then in the backgroundworker copying globalsB.gb back to globals g. I was under the assumption that globals g in background thread would be different to globals g in the UI.
The reason why I was doing this is because the logic that the background worker needs to run is identical to that of the UI logic. I have thousands of lines of code referring to g.xxxxxxx and I did not want to have to change logic
From:
if(g.MyTransSeq.transType=='m') // If Menu
{
g.MyExe.transType=g.MyTransSeq.transType
else
g.MyExe.transType='t' // Transaction
}
To:
if(currentMode='UI') // If currently running in UI
if(g.MyTransSeq.transType=='m' ) // If Menu
{
g.MyExe.transType=g.MyTransSeq.transType
else
g.MyExe.transType='t' // Transaction
}
else
//
// Job is running as backgroundworker
//
if(gb.MyTransSeq.transType=='m' ) // If Menu and
{
gb.MyExe.transType=gb.MyTransSeq.transType
else
gb.MyExe.transType='t' // Transaction
}
I currently have over 20,000 lines of code based on the existing 50 classes in globals g. To cater for g v gb will require a major rework. Is there any way around this ?regards
Pat
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks Fernando,
I'll give this a go and let you know how I get on.
regards
Pat
I'll give this a go and let you know how I get on.
regards
Pat
Very good.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hi Fernando,
sorry for delay in getting back. I have just serialized each of the classes but when I call DeepCopyGlobal(g) I get the following error. Type 'System.Data.DataRow' in Assembly 'System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c56193 4e089' is not marked as serializable."}
Tried it for just one Object, g.MyUser and had the same error. Have you come across this before ?
regards
Pat
sorry for delay in getting back. I have just serialized each of the classes but when I call DeepCopyGlobal(g) I get the following error. Type 'System.Data.DataRow' in Assembly 'System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c56193
Tried it for just one Object, g.MyUser and had the same error. Have you come across this before ?
regards
Pat
Hi Pat;
I have stated in a previous post the following,
I have stated in a previous post the following,
This part is not so simple because your class does not implement a Deepcopy which is need to copy over basic variable types as well as create NEW reference types for the reference variables. The below function will do a DeepCopy as long as all the class objects in the globals is not a sealed class and the class has been marked SerializableThe DataRow class does NOT implement the serialization of this object but the DataTable is marked as Serializable and the DataRow's are members of the DataTable and therefore you get both. So you will need to change your code to use the DataTable in place of the DataRow in Globals and in code go through the DataTable to get to the DataRow objects.
Hi Pat;
Please followup on this question.
Thanks
Fernando
Please followup on this question.
Thanks
Fernando
ASKER
Apologies for delay in responding as I was on holiday. This solution is now working nicely. Thanks for your help
Not a problem pclarke7, glad to help.