Processing中的线程出现问题,涉及到mouseClicked()函数。

huangapple go评论68阅读模式
英文:

Threading going wrong in Processing with the mouseClicked() function

问题

在我的Processing应用程序中,我监听鼠标点击事件,然后在一个按钮类中处理它们。按钮类通过调用setScreenIndex方法设置正确的内容。该函数检测是否是首次加载屏幕,然后进行设置(不会在实例化时设置,以防用户不需要时加载不必要的屏幕)。设置是通过调用一个设置函数的线程完成的。然而,当鼠标在线程忙碌时再次点击时,该线程似乎会被中断。这会导致加载屏幕无限显示(即使线程仍然被注册为活动,但似乎不再执行任何操作),或者加载屏幕完全冻结。我认为这是一个线程问题,有些东西的交互方式很奇怪,但我很迷茫,已经花了一个星期在这个问题上。

相关代码如下:

final class Main
{
  public synchronized void update()
  {
    try
    {
      if (threadToWaitFor != null)
      {
        if (!threadToWaitFor.isAlive())
        {
          threadToWaitFor = null;
          setScreenIndex(nextScreenIndex);
        }
      }
      currentScreen.update();
    }
    catch (Exception e)
    {
      logger.println("错误:主更新:" + e.getMessage());
      logger.println("堆栈跟踪:");
      e.printStackTrace(logger);
      logger.flush();
      exit();
    }
  }
  public synchronized void setScreenIndex(int screenIndex)
  {
    this.screenIndex = screenIndex;
    currentScreen = screens.get(this.screenIndex);
    if (!currentScreen.getSetup())
    {
      SetupScreenThread thread = new SetupScreenThread(currentScreen);
      thread.start();
      waitForThread(thread, screenIndex);
    }
  }
  public synchronized void waitForThread(Thread thread, int newScreenIndex)
  {
    this.threadToWaitFor = thread;
    this.nextScreenIndex = newScreenIndex;
    this.currentScreen = loadScreen;
  }
}

我尝试过检查按钮是否被调用了两次,以及相同的函数是否在同时被调用,使用了synchronizedvolatile关键字,但是没有帮助。如果不多次点击,一切都正常,行为/加载是正确的。我尝试在主类中实现一个锁,以便如果标志设置为忙碌,点击甚至不会到达Main类,但这也不起作用。

英文:

In my Processing application, I listen for mouse clicks and then handle them in a button class. The button class then sets the right content via calling setScreenIndex. The function detects if this is the first time loading the screen, and then sets it up (not set up upon instantiation so unnecessary screens arent loaded if the user doesnt need them). The setting up is done via a thread that calls a setup function. This thread however seems to cut off when the mouse is clicked again whilst it is busy. This results in either the loading screen displaying indefinitely (the thread still registers as alive even though it no longer appears to do anything), or the loading screen freezing completely. I believe this is a threading issue, and somethings interacting weirdly but I am lost and have spent a week on this problem.
Relevant code below:


final class Main
{
  public synchronized void update()
  {
    try
    {
      if (threadToWaitFor != null)
      {
        if (!threadToWaitFor.isAlive())
        {
          threadToWaitFor = null;
          setScreenIndex(nextScreenIndex);
        }
      }
      currentScreen.update();
    }
    catch (Exception e)
    {
      logger.println("ERROR: Main Update: " + e.getMessage());
      logger.println("Stack Trace: ");
      e.printStackTrace(logger);
      logger.flush();
      exit();
    }
  }
  public synchronized void setScreenIndex(int screenIndex)
  {
    this.screenIndex = screenIndex;
    currentScreen = screens.get(this.screenIndex);
    if (!currentScreen.getSetup())
    {
      SetupScreenThread thread = new SetupScreenThread(currentScreen);
      thread.start();
      waitForThread(thread, screenIndex);
    }
  }
  public synchronized void waitForThread(Thread thread, int newScreenIndex)
  {
    this.threadToWaitFor = thread;
    this.nextScreenIndex = newScreenIndex;
    this.currentScreen = loadScreen;
  }
}

I have tried checking the button isnt being called twice and that the same functions arent being called at the same time using the synchronised and volatile keywords, but this doesnt help. If you do not click multiple times, everything is fine and behaves/loads correctly. I have tried implementing a lock in the main class so that the click doesnt even reach the Main class if the flag is set to busy but this also doesnt work.

答案1

得分: 1

I understand your instructions. Here is the translated content:

我认为你解决问题的方法不太对。

你试图实现视图的懒加载,这是个好主意。但是自己尝试实现这个并不明智。

你应该使用已经存在的允许异步计算的类。Future API 是你的朋友。

    public Map<String, Future<View>> views = initViews();

    public Future<View> currentView;

    public static Map<String, Future<View>> initViews() {
        Map<String, Future<View>> screens = new HashMap<>();
        screens.put("login", null);
        screens.put("home", null);

        return screens;
    }

    public synchronized void update() throws InterruptedException {
        ...
        if (currentView.isDone()) {
            try {
                View view = currentView.get();
                // 对你的视图进行操作
            } catch (ExecutionException e) {
                // 异常在你的异步方法调用中被抛出并且没有被捕获
                throw new RuntimeException(e);
            }
        }
        ...
    }

    public synchronized void setScreenIndex(String viewName) {
        if (views.get(viewName) == null) {
            CompletableFuture<View> future = new CompletableFuture<>(() -> {
                switch (viewName) {
                    case "login":
                        return new LoginView();
                    case "home":
                        return new HomeView();
                    default:
                        throw new RuntimeException("未知视图: " + viewName);
                }
            });
            views.put(viewName, future);
            future.whenComplete((view, throwable) -> {
                        if (throwable != null) {
                            throw new RuntimeException(throwable);
                        }
                        // 更新当前视图
                        currentView = views.get(viewName);
                    }
            );
        } else
            currentView = views.get(viewName);
    }

这段代码并不是完美的。只是为了给你一个如何在你的用例中使用 Future API 的想法。

英文:

I think you are taking a wrong approach to solve your problem.

You are trying to achieve a lazy loading of your views, which is a good idea. But trying to implement this yourself is not a good idea.

You should use the already existing classes that allow async computation.
The Future API is your ally.

    public Map&lt;String, Future&lt;View&gt;&gt; views = initViews();

    public Future&lt;View&gt; currentView;

    public static Map&lt;String, Future&lt;View&gt;&gt; initViews() {
        Map&lt;String, Future&lt;View&gt;&gt; screens = new HashMap&lt;&gt;();
        screens.put(&quot;login&quot;, null);
        screens.put(&quot;home&quot;, null);

        return screens;
    }

    public synchronized void update() throws InterruptedException {
        ...
        if (currentView.isDone()) {
            try {
                View view = currentView.get();
                // do something with your view
            } catch (ExecutionException e) {
                // Exception was raised and not caught in your async method call
                throw new RuntimeException(e);
            }
        }
        ...
    }

    public synchronized void setScreenIndex(String viewName) {
        if (views.get(viewName) == null) {
            CompletableFuture&lt;View&gt; future = new CompletableFuture&lt;&gt;(() -&gt; {
                switch (viewName) {
                    case &quot;login&quot;:
                        return new LoginView();
                    case &quot;home&quot;:
                        return new HomeView();
                    default:
                        throw new RuntimeException(&quot;Unknown view: &quot; + viewName);
                }
            });
            views.put(viewName, future);
            future.whenComplete((view, throwable) -&gt; {
                        if (throwable != null) {
                            throw new RuntimeException(throwable);
                        }
                        // Update current view
                        currentView = views.get(viewName);
                    }
            );
        } else
            currentView = views.get(viewName);
    }

This code is not meant to be perfect. Just to give you an idea on how to use the Future API to your use case.

huangapple
  • 本文由 发表于 2023年5月22日 22:49:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76307387.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定