I have faced this problem while changing a code block.
List<Entity> entities = new List<Entity>();
//Some values added to the list ....
foreach(var entity in entities)
{
Thread th = new Thread(new ThreadStart(SomeMethod));
th.Start(entity);
}
SomeMethod is taking Entity and changing on it. somthing like
private void SomeMethod(Entity entity)
{
//Some operation on entity
}
This is a .NET 2.0 code, while changing it to 4.0 I did a small change i.e.
foreach(var entity in entities)
{
Thread th = new ThreadStart(() => SomeMethod(entity));
th.Start();
}
This is not working, from error it looks like entity variable getting changed between threads and resulting some null reference exception. I havnt changed anything else, as soon as code reverted to the previous way, it is fine. Can anyone explain this?
Please try to use a new local variable in such a way:
foreach(var entity in entities)
{
var copy = entity;
Thread th = new ThreadStart(() => SomeMethod(copy));
th.Start();
}
The approach with a new variable will not work if you introduce the variable in such a way
Thread th = new ThreadStart(() => {var copy = entity; SomeMethod(copy)) };
since that code will still capture the loop variable that is changed in each iteration of the loop.
Also please note that you can get the error you described if the entities collection contains the same element twice (that is two references to the same Entity object). Please make sure that it's not the case.
Related
The application was developed on ASP NET Core 3. To log user actions, I decided to use a single method in the Project class. Faced the problem of using one singleton dbContext from different threads.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("ConnectionDB");
services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddSingleton<Project>();
}
Project.cs
public async Task AddUserLog(string action, string message, int userId)
{
try
{
UserLog userLog = new UserLog()
{
Action = action,
Message = message,
UserId = userId
Datepoint = DateTime.Now
};
_dbContext.UserLog.Add(userLog);
await _dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
await AddSystemLog("Project", "AddUserLog", ex.Message);
}
}
SchemeController.cs
public class SchemeController : ControllerBase
{
private readonly Project _project;
public SchemeController(Project project)
{
_project = project;
}
[Authorize(Policy = "AdvancedControl")]
[HttpPost("[action]")]
public async Task SomeMethode()
{
for (int i = 0; i < 10; i++)
{
await _project.AddUserLog("Text", "Message", 42);
}
}
}
Already at the second iteration of the loop, I catch an exception in the AddUserLog method:
"A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext."
I suggest several solutions:
Add the log to the buffer table and then save it to the database by timer. But this is not the best way out;
Block the method while it is being saved to the database.
But I don’t like any of the options.
Please tell me the correct approach in solving this issue.
So, you trying to use shared resource (singleton Project class) to perform parallel operations (save UserLogs) while your shared resource implementation is not thread-safe (exceptions raised).
You have at lease three ways to solve this:
Do not use shared resource: register Project per scope instead of singletone;
Do not perform operations in parallel: seems hard to achieve because you making webapp and you can't force user(s) to wait
Refactor your resource to be thread-safe: add locks/mutexes/buffering... inside Project
There is no one "correct" way - all 3 are correct. Choose one you like (or combine several).
Usually using scoped dbcontext is recommended (because connections are pooled), but it's the creator of app who should decide.
While doing some testing with OrmLite I encountered some problem with multithreading.
In some cases, using my Repo from different threads I encountered a random "concurrency" error on the FieldDefinitionMap.
Reproducing the error is quite easy, just start 10 threads with massive Read / Write operations to get a random error:
'Field Definition Id was not found'
Is very important in order to reproduce the error to use the update/insert functions for the first time on the new threads.
The error comes from the function:
public virtual void SetParameterValues<T>(IDbCommand dbCmd, object obj)
I was able to solve it putting a lock on the FieldDefinitionMap like follow:
namespace ServiceStack.OrmLite
public class ModelDefinition
public Dictionary<string, FieldDefinition> FieldDefinitionMap
{
get
{
lock (this) // Locking the get works without problems
{
if (fieldDefinitionMap == null)
{
fieldDefinitionMap = new Dictionary<string, FieldDefinition>();
foreach (var fieldDef in FieldDefinitionsArray)
{
fieldDefinitionMap[fieldDef.FieldName] = fieldDef;
}
}
return fieldDefinitionMap;
}
}
}
By the way I do not know if there is any better solution.
error is : The context cannot be used while the model is being created.
I'm using this code :
Parallel.Invoke(AddDataParallel);
private void AddDataParallel()
{
Parallel.For(1001, 2001, delegate(int i)
{
User user = new User();
user.UserName = "user" + i;
_userService2.Add(user);
});
}
error :
public T Add(T entity)
{
return _entities.Add(entity);//The context cannot be used while the model is being created.
}
why ?
You seem to use only one context instance (wrapped in _userService2). But an ObjectContext (or DbContext) is not thread-safe as per MSDN. See Remarks:
The ObjectContext class is not thread safe. The integrity of data objects in an ObjectContext cannot be ensured in multithreaded scenarios.
So you have to re-design your insert scenario. Parallellization against a database is always tricky as you make yourself your own concurrent user. If you want fast inserts, take a look at BulkInsert.
I found strange hibernate behavior and I cannot explain it.
If I create an object in default thread inside transaction and make manual flush
then I cannot find it in other thread.
If I create an object in one special thread with the same conditions then everything is all right.
Here is the code that I described above:
// transaction template with propagation required
ttNew.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
Assert.assertEquals(envStDao.getAll().size(), 0);
g = new Group();
g.setDescription("trial");
// in debugger I get id = 1
groupDao.save(g);
groupDao.flush();
accDao.flush();
}
});
// second stage right after the first - searching the group
Thread t2 = new Thread(new Runnable() {
#Override
public void run() {
ttNew.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// here I get NULL!
Group gg = groupDao.get(1);
}
});
}
});
t2.start();
t2.join();
If I wrap first block of the code into thread just as former I get the group.
Are any ideas?
I run above code in junit test. Dao objects use HibernateTemplate.
Due to transaction isolation you cannot see uncommitted data in another transaction. you have two different transaction here , so one cannot see uncommitted data of another.
The default isolationist is read committed. flush dosnt mean commit. commit will be done only at the end of the transaction. so when you flush the data in first transaction the data is written to the db , but doesn’t commit, so transaction 2 cannot see it.
I don't know if this is possible at all so this is a shot in the dark.
Anyhow...
Consider having the following model:
Class Model
{
public List<string> TheList = null;
}
The List is set to null on purpose.
var model = new Model();
command.RegisterInData( model => model.TheList ); // TheList is null at this point
model.TheList = new List<string>();
model.TheList.Add("A value here");
command.Execute(); // <-- Here I want to access the new list somehow
As said, I don't know if anything like this is possible but I would like a push in the right direction.
The function desired: I would like to tell the command where to put the result before I have a concrete object.
Thanks in advance
This seems quite doable. Here is a variation with an even simpler accessor:
class Command
{
private Func<List<string>> listAccessor;
public void RegisterInData(Func<List<string>> listAccessor)
{
this.listAccessor = listAccessor;
}
public void Execute()
{
var list = this.listAccessor();
foreach (string s in list)
{
Console.Log(s);
}
}
}
// Elsewhere
var model = new Model();
command.RegisterInData(() => model.TheList);
model.TheList = new List<string>();
model.TheList.Add("A value here");
command.Execute();
You'll probably want error handling for the case where RegisterInData is not called before Execute, but you get the idea.
You simply have to delay calling the delegate passed to RegisterInData and call it (I guess) at Execute.
Could Lazy be of use here? http://msdn.microsoft.com/en-us/library/dd642331.aspx