I finally figured out what's wrong with my code, but I'm not sure how to fix it. I have some background processes running on a separate thread that perform some database maintenance tasks. Here's an exmple of what's happening:
//Both processes share the same instance of ISessionFactory
//but open separate ISessions
//This is running on it's own thread
public void ShortRunningTask()
{
using(var session = _sessionFactory.OpenSession())
{
//Do something quickly here
session.Update(myrecord);
}
}
//This is running on another thread
public void LongRunningTask()
{
using(var session = _sessionFactory.OpenSession())
{
//Do something long here
}
}
Let's say I start LongRunningTask first. While it's running I start ShortRunningTask on another thread. ShortRunningTask finishes up and closes its session. Once LongRunningTask finishes it tries to do something with it's session it created but an error get's thrown saying that the session has already been closed.
Clearly what's happening is that ISessionFactory.OpenSession() is not honoring the fact that I've opened 2 separate sessions. Closing the session opened in ShortRunningTask also closes the session in LongRunningTask How can I fix this? Please help!
Thanks!
UPDATE
So apparently everyone thinks my fix is totally wrong. So here's the configuration I am using:
_sessionFactory = Fluently.Configure()
.Database(
FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008
.ConnectionString(db => db.Is(
WikipediaMaze.Core.Properties.Settings.Default.WikipediaMazeConnection)))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<IRepository>())
.BuildSessionFactory();
I have no configuration taking place in an xml file. Should there be? What am I missing. Here's another example of how opening multiple sessions fails:
public void OpenMultipleSessionsTest()
{
using(var session1 = _sessionFactory.OpenSession())
{
var user = session1.Get<Users>().ById(1);
user.Name = "New Name";
using(var session2 = _sessionFactory.OpenSession())
{
//Do some other operation here.
}
session1.Update(user);
session1.Flush(); // Throws error 'ISession already closed!'
}
}
I figured out how to fix the problem. I setup my SessionFactory as a singleton at made it [ThreadStatic] like this:
[ThreadStatic]
private ISessionFactory _sessionFactory;
[ThreadStatic]
private bool _isInitialized;
public ISessionFactory SessionFactory
{
get
{
if(!_isInitialized)
{
//Initialize the session factory here
}
}
}
The crux of the problem is that creating sessions on separate threads from the same ISessionFactory is problematic. ISessionFactory does not like multiple ISessions being opened at the same time. Closing one, automatically closes any others that are open. Using the [ThreadStatic] attribute creates a separate ISessionFactory for each thread. This allows me to open and close ISessions on each thread without affecting the others.
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.
My C# app has lots of forms to perform various tasks. To keep things simple, I have a static class FormsCollection where the instances of each of the other forms are kept, so that they are shown and hidden from one place.
Now i was getting that classic debug time "Cross-thread" error. I tried to fix it the following way:
public class FormsCollection : Control // inherited from Control only to be
// able to call "this.Invoke"
{
public delegate void ShowFormDelegate(Form form);
public static Main mainForm;
// and many other forms...
public void ShowForm(Form form)
{
if (form.InvokeRequired)
{
ShowFormDelegate delegateFunc = new ShowFormDelegate(ShowForm);
this.Invoke(delegateFunc, new object[] { form });
}
else
{
previousForm = currentForm;
currentForm.Hide();
currentForm = form;
currentForm.Show();
}
}
}
Inside the user/caller forms, i simply make an object of FormsCollection and call the ShowForm method (almost a 100 such calls):
FormsCollection f = new FormsCollection();
f.ShowForm(FormsCollection.mainForm);
And after all this ordeal, what i get is that the same error appears at the very same spot as before! What an irony! :)
What am i doing wrong? Please help me out....
I got extremely hard-to-find answer to this problem from this slightly irrelevant page. Following is the updated code for any struggling programmers looking for a work around to this problem:
public void Show(Form nextForm)
{
Thread thread = new Thread(
new ThreadStart(() =>
{
PreviousForm.BeginInvoke(
new Action(() =>
{
PreviousForm = CurrentForm;
CurrentForm = nextForm;
PreviousForm.Hide();
CurrentForm.Show();
}
));
}
));
thread.Start();
}
This seems to have resolved the cross-thread problem that i was facing despite several work-arounds. It is working as yet without raising any exceptions and I have tested almost all the scenarios of my app going back and forth from form to form.
I have a requirement in JavaFX 2 to display the notifications on the header. These notifications are populated in the database through an external system.
When a new notification is inserted in the database, my JavaFX application should listen to the same and refresh the header.
One solution I can think about is to implement a timeline that triggers the code to retrieve the latest notification status once every fixed time period, may be once every 2 mins.
Other than this, is there any other way to achieve this? Any hints on this would be highly appreciated.
You can create a new Task that would be listening (or checking) for changes in your database. This Task would be running in a different Thread as not to block your UI. Once a change occurs, it can then update your UI.
Your code could look like this :
//... initialize your variables.
task = new DatabaseWatcher();
executor = Executors.newSingleThreadExecutor();
executor.execute(task);
And your DatabaseWatcher :
public class DatabaseWatcher extends Task<Object> {
public DatabaseWatcher() {
//initialize here
}
#Override
protected Object call() throws Exception {
//Get data from database
//if new update :
updateUI();
return null;
}
private void updateUI() {
Platform.runLater(new Runnable() {
#Override
public void run() {
//Set your new values in your UI
//Call the method in your UI to update values.
}
});
}
}
This should get you started on the right path.
See Also
Multiple FXML with Controllers, share object, more specifically this answer
javafx, update ui from another thread
Concurrency in JavaFX
I am creating a j2me application which is interacting with a database on a server. Therefore I launch a thread to connect to the server. I handle all my command actions in a separate class which implements CommandListener.
When I try to get the response the server returned I get an empty String. I have tried waiting for the thread that connects to the server to return but this makes the application unresponsive.
Below is the code I am using.
//my network worker class.
public class NetworkConnector implements Runnable {
//constructor
public NetworkConnector(String url){
//url =>server url to connect to.
Thread thread = new Thread(this);
thread.start();
}
private String serverReply="";
private String url="
//method to connect to the server and return the
//response from the server.
public String sendData(String serverUrl) {
//open streams to connect to the Server.
httpConn = (HttpConnection)
Connector.open(serverUrl,Connector.READ_WRITE, true);
httpConn.setRequestMethod(HttpConnection.GET);
inStream = httpConn.openInputStream();
int read;
while ((read = inStream.read()) != -1) {
sb.append((char) read);
}
return sb.toString();
}
public String getServerReply() {
//serverReply is a class variable.
return serverReply;
}
public void run(){
//call the send method that connects to the server.
serverResponse = sendData(Url);
}
} //end of connector class.
// this class is where all my Command Actions are
//implemented.
public class CommandActionController implements
CommandListener, DataReceiver {
public void commandAction(Command cmd, Displayable d) {
//networkMgr => is the worker class that makes
//connection to the server.
networkMgr = new NetworkConnector("http://localhost
/JsonPhp/login.php?uname=" + loginUserInfo.userName +
"&passwd=" + loginUserInfo.password);
//here am getting the response from the server.
String serverResponse = networkMgr.getServerReply();
//at this point the ServerReponse String is Empty =>
//yet the server is supposed to return some String.
System.out.println("xxxxxxxxxxxxxxxxxx
ServerResponse =" + serverResponse);
}
}//end of CommandListener class.
Your expectations about serverResponse value are wrong; it can be empty at the moment when you attempt to obtain it in commandAction with the code snippet in the question.
In order to get non-empty value, you need to redesign method NetworkConnector.getServerReply() to make it properly wait until thread finishes and server response is indeed obtained.
If you do that, however, you will also have to redesign CommandActionController to keep user interface responsive - your testing with "waiting for the thread that connects to the server" has shown just that.
Redesigned code should just launch the server connect thread and exit the commandAction as soon as possible (possibly changing to some "wait screen" to let user see what happens). Next screen update should be triggered only after server response is obtained.
Consider studying a tutorial specifically targeted on explaining how to properly design this kind applications - Networking, User Experience, and Threads:
This article explains how your MIDlet can make network connections without compromising your user interface. It includes six iterative examples that illustrate multithreaded networking and the use of a wait screen...
Here is how I'd do without java.util.concurrent:
Result handleRequest(){
final String url = // get url
Work work = new Work(url):
Thread t = new Thread(work);
t.start();
// do other stuff
if(t.isAlive()){
// still running
}
// this waits until the work is done
// and it will be safe to access work.result after
// this call has returned.
t.join();
return work.result;
}
and then
class Work implements Runnable{
final String url;
Result result;
public void run(){
this.result = // do your network stuff
}
}
You shouldn't start a thread in the constructor. Also, runnable are meant to be passed to threads. The easiest (and the "proper") way of doing this would be as follows:
Implement a Callable instead of a Runnable.
Use a ExecutorService instead of a Thread
Use Future object to do the processing asynchronously/be responsive to user
Here is a simple example.
class Stuff {
final ExecutorService exec =
Executors.newCachedExecutorService(// please see doc);
Result process(String url){
Future<Result> future = exec.submit(new Work(url));
// do some other stuff
if(future.isDone()){
// return result?
}
return future.get(); // this call will wait until result is available
}
}
and then
class Work implements Callable<Result> {
final String url;
Result call() throws Exception {
// do your network stuff here
return result;
}
}
See documentation of java.util.concurrent.ExecutorService.submit(), java.util.concurrent.Future and java.util.concurrent.Callable for details.
In a multi form .NetCF 3.5 application I'm trying create the forms in the background while the user is occupied with the previous form.
We're using Orientation Aware Control in the project
We use a wrapper class (FormController) (please let me know if I'm using the wrong terminology) to keep static references to the different forms in our application. Since we only want to create them once.
At the moment the Forms are created the first time they are used. But since this is a time consuming operation we'd like to do this in the background while the user
Application.Run(new FormController.StartUI());
class FormController{
private static object lockObj = new object();
private static bool secIsLoaded = false;
private static StartForm startForm = new StartForm();
private static SecForm m_SecForm;
static SecForm FormWorkOrderList
{
get
{
CreateSecForm();
return m_SecForm;
}
}
private static void StartUI(){
startForm.Show();
ThreadStart tsSecForm = CreateSecForm;
Thread trSecForm = new Thread(tsSecForm);
trSecForm.Priority = ThreadPriority.BelowNormal;
trSecForm.IsBackground = true;
trSecForm.Start();
return startForm;
}
private static void CreateSecForm()
{
Monitor.Enter(lockObj);
if(!secIsLoaded){
m_SecForm = new SecForm();
secIsLoaded = true;
}
Monitor.Exit(lockObj);
}
private static void GotoSecForm()
{
SecForm.Show();
StartForm.Hide();
}
When I call GotoSecForm() the program throws an excepton on SecForm.Show() with an exection with hResult: 2146233067 and no other valuable information.
The stacktrace of the exception is:
on Microsoft.AGL.Common.MISC.HandleAr(PAL_ERROR ar)
on System.Windows.Forms.Control.SuspendLayout()
on b..ctor(OrientationAwareControl control)
on Clarius.UI.OrientationAwareControl.ApplyResources(CultureInfo cultureInfo, Boolean skipThis)
on Clarius.UI.OrientationAwareControl.ApplyResources()
on Clarius.UI.OrientationAwareControl.OnLoad(EventArgs e)
on Clarius.UI.OrientationAwareControl.c(Object , EventArgs )
on System.Windows.Forms.Form.OnLoad(EventArgs e)
on System.Windows.Forms.Form._SetVisibleNotify(Boolean fVis)
on System.Windows.Forms.Control.set_Visible(Boolean value)
on System.Windows.Forms.Control.Show()
I'm quite qlueless about what's going wrong here. Can anyone help me out?
Or are there some better ways to load the forms in the background?
Let me know if any more information is needed.
You can't create forms (or safely do any manipulation of controls or forms) in background threads. They need to be created on the same thread that the message pump is running on - its just the way that Windows Forms work.
Creating the form itself shouldn't be in itself an expensive task. My advice would be to perform any expensive computations needed to display the form in a background thread, and then pass the result of those computations back to the main message pump in order to create and display the form itself.
(Half way through writing this I realised that this question is about windows mobile, however I'm 99% sure that the above still applies in this situation)